题目链接:点击查看
题目大意:给出一个环状的灯泡,标号为 1 ~ n ,初始时全部为熄灭状态,现在两个人开始玩游戏,第一个人可以选择继续游戏,也可以选择结束游戏,继续游戏的话首先选择一个正整数 k ,然后点亮 k 个熄灭的灯泡,第二个人可以选择连续的 k 个灯泡将其全部熄灭,如此往复,设 R( n ) 是 n 个灯泡的情况下,经过两人的数轮操作后可以亮着的最大灯泡数,第一个人可以在第二个人操作完后,亮着的灯泡不小于 R( n ) 时结束游戏
题目分析:一道需要制定贪心策略的题目,首先需要求出 R( n ) 为多少,首先假设当前亮着的灯泡数为 x ,第一个人选择的数字为 k ,这样下一轮第一个人经过操作后,亮着的灯泡数变为了 x + k ,因为第二个人可以选择 k 个连续的灯泡熄灭,如果本轮操作可以提供贡献的话,必须保证这 x + k 中最长的连续的亮着的灯泡是小于等于 k - 1 个的,这样第二个人熄灭连续的 k 个灯泡后,本轮的贡献仍然是 x + 1 ,我们需要求出这个 x
因为一共 x + k 个亮着的灯泡,极限情况就是每 k - 1 个亮着的灯泡分成一组,又因为最长的连续的灯泡个数必须小于等于 k - 1 ,所以每两段之间需要有一个熄灭的灯泡隔开,画个图就是这样的:
浅蓝色表示的是环状的灯泡分布,深蓝色的是连续的 k - 1 个亮着的灯泡,红色的是熄灭的灯泡
这样显然熄灭的灯泡个数为 ,亮着的灯泡个数为 x + k ,总灯泡个数为 n ,所以列出不等式:
解得 ,因为我们这个 x 的含义是,最后一次放之前亮着的灯的个数,所以在最后一次操作时,又放置了 k 个灯泡,对手拿走了 k - 1 个灯泡,此时达到 R( n ) 的局面,换句话说,其实 才对
这样我们可以枚举 k 维护出最大的 R( n ) ,找到 k 后贪心就好了,就像上面那个图一样,为了方便处理,我们设起点从 0 开始,不能放置的位置都为 i % k == 0 ,其余位置贪心填满就好了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
set<int>s;
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
int mmax=0,k=1;
for(int i=1;i<=n;i++)
{
int temp=n-(n+i-1)/i-i+1;
if(temp>mmax)
{
mmax=temp;
k=i;
}
}
for(int i=0;i<n;i++)
if(i%k)
s.insert(i);
while(s.size()>=k)
{
printf("%d ",k);
for(int i=0;i<k;i++)
{
printf(" %d",*s.begin()+1);
s.erase(s.begin());
}
puts("");
fflush(stdout);
int pos;
scanf("%d",&pos);
pos--;
for(int i=0;i<k;i++)
if((pos+i)%n%k)
s.insert((pos+i)%n);
}
puts("0");
fflush(stdout);
return 0;
}