1. 程式人生 > 實用技巧 >題解 CF1375F Integer Game(構造,博弈)

題解 CF1375F Integer Game(構造,博弈)

題目連結

嘗試通過構造,讓先手贏。

考慮什麼情況下,後手不得不讓兩個堆數量相等。假設某個局面下,三堆裡分別有\(a,b,c\)個石頭,不妨設\(a>b>c\)。那麼如果上一輪的操作物件是\(a\)(這一輪不能對\(a\)操作),且\(a-b=b-c\)。那麼此時,如果先手報:\(a-b\),後手就不得不讓兩堆數量相等:他要麼把\(b\)變成\(a\),要麼把\(c\)變成\(b\)

總結一下這個條件,就是讓上一次的操作物件,變成當前最大的數,且當前三個數從大到小依次差相等。那麼此時,只需要一次操作,先手就贏了。如圖:

我們進一步思考,如何達到上述的局面呢?還是假設\(a>b>c\)

\(a\)在上一輪被用過。設\(x=a-b\), \(y=a-c\)。此時先手可以報\(x+y\)。那麼後手要麼讓\(b\texttt{+=}x+y\),要麼讓\(c\texttt{+=}x+y\),無論哪種情況,被操作的數都會成為最大值,且三個數從大到小依次差相等!於是就變成了上一種必勝的局面。

由此可知,只要上一次的操作物件是當前最大的數,先手就一定能用兩次操作獲勝。

這個條件比第一個條件弱很多,這個局面也很好達到。一開始的時候,不妨還是設\(a>b>c\),先報\(a-b\)。此時,\(a-b\)要麼被作用於\(a\)上(達到效果,使上一次操作物件是當前最大的數),要麼被作用於\(c\)

上。如果被作用於\(c\)上,下一輪就只能操作\(a\)\(b\),我們再報\(a-b\),後手不能對\(b\)操作(否則\(a=b\)就直接輸了),所以後手只能對\(a\)操作,達到了我們希望的局面。

綜上所述,我們選先手,在\(4\)步以內,必能獲勝。

參考程式碼:

//problem:F
#include <bits/stdc++.h>
using namespace std;
 
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
 
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
 
ll a[4];
void work(){
	//上一次的操作物件,在操作後是最大值
	for(int i=1;i<=3;++i){
		if(a[i]==max(a[1],max(a[2],a[3]))){
			int j=1,k=2;
			if(i==j)j=3;
			if(i==k)k=3;
			cout<<(a[i]-a[j])+(a[i]-a[k])<<endl;
			int l;
			cin>>l;
			assert(l!=i);
			if(l==0)return;
			if(l==k)swap(j,k);
			//l=j
			a[j]+=(a[i]-a[j])+(a[i]-a[k]);
			cout<<a[i]-a[k]<<endl;
			cin>>l;
			assert(l==0);
			return;
		}
	}
}
int main() {
	cin>>a[1]>>a[2]>>a[3];
	cout<<"First"<<endl;
	for(int i=1;i<=3;++i){
		if(a[i]==max(a[1],max(a[2],a[3]))){
			int j=1;
			if(i==1)j=2;
			cout<<a[i]-a[j]<<endl;
			int k;
			cin>>k;
			a[k]+=a[i]-a[j];
			assert(k!=j);
			if(k==0)return 0;
			else if(k==i)work();
			else{
				if(a[k]>a[i])work();
				else{
					cout<<a[i]-a[j]<<endl;
					int l;
					cin>>l;
					a[l]+=a[i]-a[j];
					assert(l!=k);
					assert(l!=j);
					if(l==0)return 0;
					else work();
				}
			}
			return 0;
		}
	}
	return 0;
}