题意 : 有一个三角形架,两个人轮流游戏,每次都可以涂黑一条边,如果涂黑这条边之后恰好凑好了一个小三角形,就可以继续画边,给出初始局势,问在双方都足够理智的情况下,先手胜还是后手胜,或者平局
这种博弈问题,最近见了几道,总的来说是需要做到:
①选择合适的方式记录状态(即局势),且记录的时候要考虑双方的双向性,比如“A吃了三个糖果,B吃了五个糖果”与“A吃了五个糖果,B吃了三个糖果”是两个对称的,不同的局势,一般要记为两种
②可以用dp来记录不同局势下的最优解,同时能做到局面换手
③进行局面转移,可以用dfs来进行搜索,dfs做这个事情很适合,即选择搜索树的临节点
这道题的状态情况比较多,因此用状压dp来做,即18位二进制位,第i位为1,则代表第i条边涂黑了。形成的十进制数记为now
那么就用dp[now][0]记录now所代表的局势下,该先手画边的情形下的最优解,对应的,dp[now][1]是后手的最优解。
用dfs搜索局势,也是依赖于这个题目的规模很小,如果涂了某条边能画出更多的三角形,当前人可以继续画边,因此就表示为
dp[now][state]=max(dp[now][state],dfs(next,state)+add);
这里state表示当前是先手还是后手,next是画了这条边之后的局势,add是当前手画边之后多出来的三角形数目,容易写错的是dfs(next,state)中的state,不用改变
如果不能画出更多的三角形,并不代表这个状态不用遍历了,而是:
dp[now][state]=max(dp[now][state],last-dfs(next,1-state));
用1-state代表局面换手
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <string>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#define LL long long
using namespace std;
const double pi=acos(-1);
const int Mod=1e9+7;
const double eps=1e-9;
int dp[1<<22][2];//store the answer for each status
int vis[1<<22][2];//mark when a state is calculated,and notice that state is bidirectional.
int calcu(int n)
{
int ans=0;
bool vis[20]={false};
for(int i=1;i<=18;i++)
{
if((n>>i)&1)vis[i]=true;
}
for(int i=0;i<6;i++)
{
if(vis[i*3+1]&&vis[i*3+2]&&vis[i*3+3])
ans++;
}
if(vis[3]&&vis[5]&&vis[7])ans++;
if(vis[6]&&vis[11]&&vis[13])ans++;
if(vis[9]&&vis[14]&&vis[16])ans++;
return 9-ans;
}
int dfs(int now,int state)
{
if(vis[now][state])return dp[now][state];
int last=calcu(now);
vis[now][state]=1;
for(int i=1;i<=18;i++)
{
if(((now>>i)&1)==0)//edge i has not be colored.
{
int next=now|(1<<i);
int tmp=calcu(now)-calcu(next);
if(tmp>0)
dp[now][state]=max(dp[now][state],dfs(next,state)+tmp);
else
dp[now][state]=max(dp[now][state],last-dfs(next,1-state));
}
}
return dp[now][state];
}
int n;
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int now=0;
int edge=0;
while(cin>>n)
{
if(n==0)break;
now=0;
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
while(n--)
{
cin>>edge;
now|=(1<<edge);
}
int last=calcu(now);
//cout<<now<<" "<<last<<endl;
int numa,numb;
numa=dfs(now,0);
numb=last-numa;
//cout<<numa<<" "<<numb<<endl;
if(numa>numb)cout<<"Andy wins"<<endl;
else if(numa<numb)cout<<"Ralph wins"<<endl;
else cout<<"Draw"<<endl;
}
}