zoj 3802 Easy 2048 Again(状压DP)

来源:互联网 发布:stc12c5a32s2数据手册 编辑:程序博客网 时间:2024/09/21 09:21

题意:从左到右依次选一些数放入队列,在队列中,如果相邻两个数相同,那么可以进行合并,合并的结果为两个数的和,最终的得分等于放入的数的和再加上每一次合并后得到的数的和,求最终能得到的最大的数。

思路:刚开始想了想dp,但是状态不会表示,于是想了半天贪心Orz……搞了半天还真是dp,真是无语。。。通过观察可以发现对于已经选择的数,如果出现了升序,那么前面的就没有用了,比如,当前的序列是8 4 2 16,最后一个数是16,那么后面无论怎么合并,值都要大于16,因此前面的状态就没有必要知道了。因此,状态就可以用一个二进制数来表示,由于都是降序,顺序也确定了,接下来dp就容易了~


代码:

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<map>#include<queue>#include<stack>#include<set>#include<cmath>#include<vector>#define inf 0x3f3f3f3f#define Inf 0x3FFFFFFFFFFFFFFFLL#define eps 1e-9#define pi acos(-1.0)using namespace std;typedef long long ll;map<ll,ll>dp[2];map<ll,ll>:: iterator it;inline void Update(int p,ll s,ll w){    if(dp[p].find(s) != dp[p].end())        dp[p][s] = max(dp[p][s],w);    else dp[p][s] = w;}void cal(ll val,int p){    dp[p].clear();    ll state,w,v;    for(it = dp[p^1].begin();it != dp[p^1].end();it++)    {        state = it -> first;        w = it -> second;        v = val;        Update(p,state,w);        w += v;        if(state & (v-1))        {            Update(p,v,w);        }        else        {            while(state & v)            {                state ^= v;                v <<= 1;                w += v;            }            state |= v;            Update(p,state,w);        }    }}int main(){//    freopen("in.txt","r",stdin);//    freopen("out.txt","w",stdout);    int t,n;    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        dp[0].clear();        dp[1].clear();        dp[0][0] = 0;        int val;        for(int i = 1;i <= n;++i)        {            scanf("%d",&val);            cal(val,i&1);        }        ll ans = 0;        for(it = dp[n&1].begin();it != dp[n&1].end();it++)            ans = max(ans,it->second);        printf("%lld\n",ans);    }    return 0;}


0 0
原创粉丝点击