题解 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 exttt{+=}x+y),要么让(c exttt{+=}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;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/13246102.html