HDU 6196 happy happy happy 爆搜加剪枝
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6196
题意:给你长度为n的序列,爸爸和儿子玩一个游戏,儿子先手,儿子每次都选择最左边与最右边最大的那个拿走(若左右相等拿左边),爸爸可以任意拿最左边或者最右边。
解法:膜一发题解:http://blog.csdn.net/snowy_smile/article/details/77929954
首先,我们DP两个东西—— 1, mx[l][r]表示对于区间[l, r],儿子先手,爸爸所能拿到的最大价值差值(差值是爸爸减儿子) 2, mn[l][r]表示对于区间[l, r],儿子先手,爸爸所能拿到的最小价值差值(差值是爸爸减儿子) 那么—— 我们尝试使用搜索解决这个问题 dfs(l, r, dif)表示当前还没有取的区间范围是[l, r],儿子先手,此时爸爸减儿子的差值为dif。 那么—— 1,这时先考虑剪枝—— 设置初始ANS = -inf; void dfs(int l, int r, int dif) { if (dif + mn[l][r] >= 0) return; //哪怕取一个最小值,都会赢了儿子,是个无效状态 if (dif + mx[l][r] <= ANS) return; //哪怕取一个最大值,差值都依然太小了,最优性剪枝 if (dif + mx[l][r] < 0) //取一个最大值,使得差值尽可能小,最优性剪枝 { gmax(ANS, dif + mx[l][r]); } } 2,再模拟儿子的操作,获得新的(l, r, dif) 3,接着需要进一步地考虑爸爸的操作—— <1>取l,变成dfs(l + 1, r, dif + a[l]); <2>取r,变成dfs(l, r - 1, dif + a[r]); 4,考虑如何获得DP数组mn[][]和mx[][]—— mn[i][i - 1] = mx[i][i - 1] = 0; for(int l = n; l >= 1; --l) { for(int r = l; r <= n; ++r) { 先模拟儿子的操作,获得新的(ll, rr) 然后考虑父亲的操作—— 1,取ll: gmax(mx[l][r], a[ll] + mx[ll + 1][rr]); gmin(mn[l][r], a[ll] + mn[ll + 1][rr]); 2,取rr: gmax(mx[l][r], a[rr] + mx[ll][rr - 1]); gmin(mn[l][r], a[rr] + mn[ll][rr - 1]); } }
题解摘自上面的博客,我直接搜索T了。然后加入桑心病况的卡时间的剪枝,可以跑到0ms。。。orz
#include <bits/stdc++.h> using namespace std; const int maxn = 110; const int inf = 0x3f3f3f3f; //mx[l][r]表示对于区间[l,r],儿子先手,爸爸能拿到的最大价值差值(差值是爸爸减去儿子) //mn[l][r]表示对于区间[l,r],儿子先手,爸爸能拿到的最小价值差值(差值是爸爸减去儿子) int LIM = 0.5*CLOCKS_PER_SEC; int ST; int n, a[maxn], mn[maxn][maxn], mx[maxn][maxn]; void pre_deal(){ memset(mn, 0x7f, sizeof(mn)); memset(mx, 0x80, sizeof(mx)); for(int i=1; i<=n+1; i++) mn[i][i-1]=mx[i][i-1]=0; for(int l=n; l>=1; l--){ for(int r=l; r<=n; r++){ int newl=l, newr = r, sub; if(a[newl]>=a[newr]) sub=a[newl++]; else sub=a[newr--]; mx[l][r] = max(mx[l][r], a[newl]+mx[newl+1][newr]-sub); mx[l][r] = max(mx[l][r], a[newr]+mx[newl][newr-1]-sub); mn[l][r] = min(mn[l][r], a[newl]+mn[newl+1][newr]-sub); mn[l][r] = min(mn[l][r], a[newr]+mn[newl][newr-1]-sub); } } } int ans; void dfs(int l, int r, int dif) { if(l>r){ ans = max(ans, dif); return; } if(dif+mn[l][r]>=0) return; if(dif+mx[l][r]<=ans) return; if(dif+mx[l][r]<0){ ans = max(ans, dif+mx[l][r]); return; } if(clock()-ST>LIM) return; int sub; if(a[l]>=a[r]) sub=a[l++]; else sub=a[r--]; dfs(l+1, r, dif+a[l]-sub); dfs(l, r-1, dif+a[r]-sub); } int main() { while(~scanf("%d", &n)) { for(int i=1; i<=n; i++) scanf("%d", &a[i]); pre_deal(); ST = clock(); ans = -inf; dfs(1, n, 0); if(ans == -inf){ puts("The child will be unhappy..."); }else{ printf("%d\n", -ans); } } return 0; }