您的位置:首页 > 大数据 > 人工智能

zoj 3802 Easy 2048 Again (动态规划)

2014-08-27 12:49 204 查看
题目大意:

有一串由 2 4 8 16 组成的数字,要求从左往右依次取 若干个数字,使得最后的价值最大。获得的价值有两部分组成,一个是加进去的价值,另外一个是合并后的价值,跟2048相似的地方是如果两个相邻的数字一样,那么他们就会合并。比如:对于数组  2 8 4 8  如果选择 2 8 4 8  那么他的价值就是2+8+4+8 = 22(因为没有可以合并的数字)但是,如果选择2 8 8(就是对于数字4不选择) 那么他的价值就是 2+8+8  + 16  = 34 (前面的2+8+8是加进去的数字的和,后面的16是相邻两个数合并后的值)
。对于2 8 4 8 这组数据,34的价值最大,所以输出34.

解题思路:

这是一道状态压缩的动态规划题目。从题目的描述中,我们可以看到:如果加进去的一个数字,比他前面的一个数字要大,那么他就不可能与前面的数字进行合并。然后,我们可以用dp[len][num] 来表示相应的值,其中第一位 len 表示 当前是第 len 个数字。第二位的num 用二进制的形式保存,如果num的值是  0110 就表示当前保存的数字是 X 4 2 其中。我们的dp 里面保存的是连续递减的数。相应的状态转移方程就变成了:

1.如果当前这个数字比它前面的一个数字大,那么num就变成它;

2.如果当前这个数字跟它前面一个数字一样,也就是说,这个数字可以进行合并,那么就合并它,num值变成合并后的dp里面递减的数字,直到不能进行合并为止,dp加上相对应的价值;

3.如果当前这个数字比它前面的一个数字小,也就是说,这个数字可以跟前面的状态形成递减,那么相应的dp就有num状态转移到num|x 状态 其中x表示当前这个数字。

4.我这边的dp值如果等于 -1 表示该状态不能达到

由于最多只有500个数字,即使每个数字都是16最大也就8000 所以,num 开成8300 就足够了。

这个题目时间限制比较紧,所以memset 一下子初始化所有的dp值会超时,如果我们每次都初始化一维效果可能会好很多。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int dp[555][8300];
int main()
{
int T,n,m,i,j;
int a[555];
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
memset(dp[0],-1,sizeof(dp[0]));
dp[0][0]=0;
for(i=1; i<=n; i++)
{
memset(dp[i],-1,sizeof(dp[i]));
for(j=0; j<=8200; j+=2)
{
//如果之前这个状态是不能达到的那么就跳过
if(dp[i-1][j]==-1)continue;
//该数比它前面一个数字大
if(((a[i]-1)&j)!=0) dp[i][a[i]]=max(dp[i][a[i]],dp[i-1][j]+a[i]);
//该数恰好可以与它前面一个数字合并
else if((a[i]&j)!=0 )
{
int tem = a[i];
int sum = tem ;
int xx=j;
while((tem&xx)!=0)
{
xx=xx-tem;
tem = tem<<1;
sum += tem ;
}
dp[i][xx|tem] = max (dp[i][xx|tem],dp[i-1][j]+sum);
}
else dp[i][j|a[i]]=max(dp[i][j|a[i]],dp[i-1][j]+a[i]);
}
for(j=0; j<8200; j+=2)
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}

int ans =0;
for(j=0; j<8200; j+=2)
ans=max(ans,dp
[j]);
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息