Gym 100015 F Fighting for Triangles 博弈,状压dp

来源:互联网 发布:淘宝五金批发市场 编辑:程序博客网 时间:2024/05/18 00:02

题意 : 有一个三角形架,两个人轮流游戏,每次都可以涂黑一条边,如果涂黑这条边之后恰好凑好了一个小三角形,就可以继续画边,给出初始局势,问在双方都足够理智的情况下,先手胜还是后手胜,或者平局

这种博弈问题,最近见了几道,总的来说是需要做到:

①选择合适的方式记录状态(即局势),且记录的时候要考虑双方的双向性,比如“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 longusing 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 statusint 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;    }}


0 0
原创粉丝点击