題目鏈接:點擊查看
題目大意:給出一個環狀的燈泡,標號爲 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;
}