您的位置:首页 > 其它

100道动态规划——5 UVA 10118 Free Candies 记忆化搜索 以及 证明状态

2016-10-30 12:39 701 查看
好吧,这道题目我没想出来。。。这道题我觉得和我之前做过的一道题目有点像,100道动态规划3 UVA 1625 http://blog.csdn.net/good_night_sion_/article/details/52918040 都是从顶部拿一些东西出来,只不过是代价函数不同而已。根据那道题目的思想,这道题的状态定义至少是4维的——需要把4个堆的顶部拿了多少个元素的这个状态包含进来。然后我就一直纠结这样一个问题,在1,2,3,4个堆分别拿了i,j,k,l个糖果的时候篮子里面的糖果数量以及颜色是不是唯一的。因为这个问题的答案将会涉及到我的数组还要再开几维的问题。至于是采用记忆化搜索还是递推或者刷表的问题,我觉得在这道题目上采用记忆化搜索是比较合适的,原因是在我们把0,0,0,0作为起点的情况下,末状态不是很好确定。而递推和刷表都是采用循环的方式,将跑到很多无效的状态里面去。

接下来是我的证明,在1,2,3,4个堆拿了i,j,k,l的糖果的时候篮子里面的糖果数量和颜色是唯一的

假设存在两种不同的拿法使得拿了i,j,k,l颗糖果之后篮子里面的状态不一样,即此时一定存在一种拿法比另外一种拿法要多至少一种颜色,无妨设A拿法比B拿法多一种a颜色。

因为两种拿法都把i,j,k,l个糖果拿完了,因此每个颜色的糖果的总数是一样的。A拿法在拿完之后还剩下一个a颜色,因此a颜色的糖果必然是奇数的。但是B拿法也是在拿完之后没有a颜色,因此a颜色是偶数的。a颜色既是奇数又是偶数,矛盾。因此状态唯一。因此在定义状态的时候只需要4个下标即可唯一描述这个状态。

接下来是记忆化搜索的代码,pos代表在其顶部拿了多少个糖果,candy表示篮子里是不是有对应的糖果。dp[i][j][k][l]代表在其顶部拿了i,j,k,l个后最多还可以拿走多少对糖果

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int n,dp[45][45][45][45],arr[4][45],pos[4];
bool candy[25];

int search(int cnt);

int main(){
while(scanf("%d",&n)&&n){
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
for(int k=0;k<=n;++k)
for(int l=0;l<=n;++l)
dp[i][j][k][l]=-1;

for(int i=0;i<n;++i)
for(int j=0;j<4;++j)
scanf("%d",&arr[j][i]);

printf("%d\n",search(0));
pos[0]=pos[1]=pos[2]=pos[3]=0;

}
return 0;
}

int search(int cnt){
int &te=dp[pos[0]][pos[1]][pos[2]][pos[3]];
if(te!=-1)
return te;
if(cnt==5)
return te=0;

te=0;
for(int i=0,color;i<4;++i){
if(pos[i]==n)continue;
color=arr[i][pos[i]];
++pos[i];
if(candy[color]){
candy[color]=false;
te=max(te,search(cnt-1)+1);
candy[color]=true;
}
else{
candy[color]=true;
te=max(te,search(cnt+1));
candy[color]=false;
}
--pos[i];
}

return te;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐