博弈论(2)DP/记忆化搜索

博弈问题的话,假设两个人都极度聪明,都会采取最优策略,那么就是也知道了对方也和自己一样聪明,我采取最优策略后,对方也会根据当前状态做出最优策略,简而言之,就是每个玩家都从第一步棋看到了最后一步棋。

先手控局,棋还没下,就已经知道走哪一步会获得什么样的结果。

那么,现代化的我们就可以用计算机根据每个状态下必取最优策略的原则,提前搜索出所有情况。

再次强调,最优是针对的状态,上文已经说过两人都是满智力奇才,面临什么样的状态就代表面临着什么样的结局。

常用算法:记忆化搜索,状压DP。

根据我的经验,博弈都是逆推,这也和上面提到的博弈的性质有关,对当前状态进行决策时,必然要先知道所有后续状态的最优嘛。

例题:

http://poj.org/problem?id=1678

题意:有N个数,有一个区间[A,B],先手先取一个数x(必须A<=x<=B),后手取一个数y(必须A+x<=y<=B+x),再先手取一个数z(必须A+y<=z<=B+y),,直到不能取,求先手所取数之和-后手所取数之和

把先手当做收益,后手当做损失,那么这题就好比求先手所面临状态的最大收益

发现这个题后面的状态不能影响前面的状态,那么便可以记忆化搜索,时间复杂度不高。

这里的状态就是区间,决策就是取一个在这个区间里的数。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int inf=1000000000;
 6 int used[30005],dp[30005],a0,b0;
 7 int dfs(int a,int b,int x)//已选择x, 【a,b】为对方面临的状态 
 8 {    if(dp[x]!=-inf) return dp[x];
 9     int num=0,res=-inf; 
10     for(int i=a;i<=b;i++)//对方在【a,b】所能达到的最大收益 
11         {    if(!used[i]) continue;
12             used[i]--;
13             res=max(res,dfs(a0+i,b0+i,i));
14             
15             used[i]++;
16             num++;
17         }
18     if(num==0) return dp[x]=x;
19     else return dp[x]=x-res;
20 }
21 int main()
22 {    int T;
23     scanf("%d",&T);
24     while(T--)
25     {    int n;
26         memset(used,0,sizeof(used));
27         for(int i=1;i<=10000;i++)
28             dp[i]=-inf;
29             
30         scanf("%d%d%d",&n,&a0,&b0);
31         for(int i=0;i<n;i++)
32         {
33             int x;
34             scanf("%d",&x);
35             if(x>0) used[x]++;
36         }
37         int res=-inf,num=0;
38         for(int i=a0;i<=b0;i++)
39         {    if(!used[i]) continue;
40             used[i]--;
41             res=max(res,dfs(a0+i,b0+i,i));
42             
43             used[i]++;
44             num++;
45         }
46         if(num==0) res=0;
47         printf("%d\n",res);
48         
49         
50         
51     }
52     
53     
54     
55     
56     return 0;
57 }

 

posted @ 2018-04-03 21:40  hzhuan  阅读(222)  评论(0编辑  收藏  举报