Codeforces 1368F

这题也太新颖了吧.. 交互博弈 以前一直以为交互只能出二分 

题意:长度为n的环形灯 玩家有两种操作 结束游戏 或者选择k个灯点亮 每次这个k是玩家自己选的

   玩家操作后让电脑操作 电脑选择一个最优的点x 然后关掉从x开始的连续k个灯

   玩家想要点亮更多的灯 电脑则相反

   让你来操作 如果达到了理论上最多灯的状态就算ac了

题解:模拟一下操作就找到规律了..

   如果玩家上一次操作点亮了k个灯 如果当前连续亮灯的最长序列为x <= k

   那么电脑操作了之后 至少会多点亮一个灯 就这样一直贪心下去.. 

   所以我们一开始枚举 最长的连续亮灯长度

   举个例子枚举的最长是2 就类似110110110.. 的把每个灯标记 1就标记为最后要点亮的灯

   再比如n=11的时候 枚举的最长=2 11011011010 注意n和1相邻一定为0

   然后模拟一下就发现这种情况 能最多点亮5个 = (n/3)*(2) + (n%3-1) - 2

   k = 7 11011011010 -> 00000001010

   k = 5 11011011010 -> 00000011010

   k = 4 11011011010 -> 00001011010

   k = 3 11011011010 -> 00011011010

   之后无论如何也不能点亮更多灯了

   然后枚举每一个长度 选一个最优的

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <string.h>
#include <string>
using namespace std;
 
int vis[1005];
int now[1005];
vector<int> g;
int main() {
    int n;
    cin>>n;

    int ans = 0;
    int fz = 2;
    for(int i = 2; i <= n / 2; i++) {
        int res = (n / i) * (i - 1);
        if(n % i != 0) res += n % i - 1;
        res -= (i - 1);
        if(res > ans) ans = res, fz = i; 
    }
    //cout << ans << endl;
    for(int i = 1; i <= n; i++) if(i % fz) vis[i] = 1;
    vis[n] = 0;
    
    int lask = 0;
    int x = 0;
    while(1) {
        if(x == -1) break;
        int rr = 0;
        int pos = x;
        while(lask--) {
            now[pos] = 0;
            pos++;
            if(pos > n) pos = 1;
        }
        for(int i = 1; i <= n; i++) if(now[i]) rr++;
        
        if(rr >= ans) {
            puts("0");
            cout.flush();
            break;
        }

        g.clear();
        for(int i = 1; i <= n; i++) {
            if(!now[i] && vis[i]) {
                now[i] = 1;
                g.push_back(i);
            }
        }
        lask = g.size();
        printf("%d", lask);
        for(int i = 0; i < lask; i++) printf(" %d", g[i]); puts("");
        cout.flush();
        cin>>x;
    }
    return 0;
}
View Code

   

原文地址:https://www.cnblogs.com/lwqq3/p/13162791.html