Codeforces 1368F - Lamps on a Circle (交互博弈)
阅读原文时间:2023年07月11日阅读:1

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

题意:长度为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
#include
#include
#include
#include
#include
#include
#include
using namespace std;

int vis[1005];
int now[1005];
vector 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;  

}