链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5334
题意:一个长度为500的数列。每一个数可能是2,4,8。16。从第一个開始取,每一个数能够选择取或者不取。假设取了x。这个数和之前的数假设同样。那么两个数会合并成为2*x而且收起来,假设不同就把这个数收起来。
思路:比方像要凑成2048,必须拥有一个1024,512,256,128,64,32,16,8,4,2中某段从1024開始的连续递减序列,然后再取得一个和末尾数同样的数才干连续合并成为2048。
所以状态压缩仅仅须要记录递减序列的状态存在情况进行压缩就可以。DP[i][j]记录的是第i位置所取到的j情况递减序列,第1位是2。第2位是4,第3位8..(500个16的极限情况也仅仅有8000。所以最多仅仅须要12位,也就是最高位4096来记录。所以仅仅有4095种情况。由于不存在1和0的情况).用一个二进制数来表示这些位是否有取到。问题关键在于假设将第i位合并进去,必须保证该状态下第1~i-1位都是0。否则就不是连续递减序列。
P.S.比赛时候想这道题想的复杂了。比方2 2 2 这样的情况我觉得还要考虑是合并成2 4,还是合并成4 2两种状况。实际上不须要。由于没取一个数就进行自己主动合并,而不是要我决定是否合并。
代码:
#include <algorithm> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <ctime> #include <ctype.h> #include <iostream> #include <map> #include <queue> #include <set> #include <stack> #include <string> #include <vector> #define eps 1e-8 #define INF 0x7fffffff #define maxn 10005 #define PI acos(-1.0) #define seed 31//131,1313 typedef long long LL; typedef unsigned long long ULL; using namespace std; int dp[505][4100]; int a[505],Pow[15]; map < int,int > M; void init() { memset(dp,-1,sizeof(dp)); M[2]=1; int pos=2; for(int i=2; i<=12; i++) { pos*=2; M[pos]=i; } return ; } int main() { int T,tot; scanf("%d",&T); while(T--) { init(); scanf("%d",&tot); for(int i=1; i<=tot; i++) scanf("%d",&a[i]); dp[1][a[1]>>1]=a[1]; for(int i=2; i<=tot; i++) { int dn=(a[i]>>1)-1; dp[i-1][0]=0; for(int j=0; j<4096; j++) { if(dp[i-1][j]>dp[i][j]) dp[i][j]=dp[i-1][j]; if((j&dn)==0&&dp[i-1][j]!=-1) { int ans=0; int k=a[i]>>1; while(1) { if((k&j)==0) break; k*=2; ans+=k*2; } int next=j-(ans>>2)+k; dp[i][next]=max(dp[i-1][j]+ans+a[i],dp[i][next]); } else if((j&dn)!=0&&dp[i-1][j]!=-1) { dp[i][dn+1]=max(dp[i][dn+1],dp[i-1][j]+a[i]); } } } int ans=0; for(int i=0; i<4096; i++) { ans=max(ans,dp[tot][i]); } printf("%d ",ans); } return 0; }
版权声明:本文博客原创文章,博客,未经同意,不得转载。