HDU-5691 Sitting in Line(状压DP)

题意

元素个数为 n n a 数组,有些数字位置在数列中的位置已经确定,现在要任意的将没确定位置的数排进数列,求 max{a1a2+a2a3+a3a4+...+an1an} m a x { a 1 a 2 + a 2 a 3 + a 3 a 4 + . . . + a n − 1 a n }
1n16 1 ≤ n ≤ 16

思路

在数列已经被钦定的位置标上数的下标,然后按顺序添数用 dpi,j d p i , j 排进选了 i i 这些数,最后一个数是 j 时的最大值。在从 0 0 枚举到 (1<<n)1 的时候,只要预处理一个 cnt c n t 表示某一个数二进制有多少个 1 1 <script type="math/tex" id="MathJax-Element-537">1</script> ,就可以随时知道接下来该填哪个位置,就可以判断改选什么数了。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define tomax(a,b) (a=max(a,b))
#define lowbit(x) ((x)&-(x))
typedef long long LL;
using namespace std;
int a[18],p[18],det[18];
int cnt[(1<<16)+3],bin[(1<<16)+3];
LL dp[(1<<16)+3][18];

int main()
{
    FOR(i,1,1<<16)cnt[i]=cnt[i-lowbit(i)]+1;
    FOR(i,1,16)bin[1<<i]=i;
    int T;
    scanf("%d",&T);
    FOR(Ti,1,T)
    {
        int n;
        scanf("%d",&n);
        memset(det,-1,sizeof(det));
        memset(dp,0xc0,sizeof(dp));
        FOR(i,0,n-1)
        {
            scanf("%d%d",&a[i],&p[i]);
            if(~p[i])det[p[i]]=i;
        }
        if(~det[0])dp[1<<det[0]][det[0]]=0;
        else FOR(i,0,n-1)if(!~p[i])dp[1<<i][i]=0;
        FOR(i,0,(1<<n)-1)
            for(int j=i;j;j^=lowbit(j))
            {
                if(!~det[cnt[i]])
                    for(int k=(~i)&(1<<n)-1;k;k^=lowbit(k))
                    {
                        if(!~p[bin[lowbit(k)]])
                            tomax(dp[i^lowbit(k)][bin[lowbit(k)]],dp[i][bin[lowbit(j)]]+a[bin[lowbit(j)]]*a[bin[lowbit(k)]]);
                    }
                else tomax(dp[i^(1<<det[cnt[i]])][det[cnt[i]]],dp[i][bin[lowbit(j)]]+a[bin[lowbit(j)]]*a[det[cnt[i]]]);
            }
        LL ans=-9e18;
        FOR(i,1,n)ans=max(ans,dp[(1<<n)-1][i]);
        printf("Case #%d:\n%lld\n",Ti,ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值