HDU 5691 --tsp变型

题意:

有n(n<=16)个数字需要排列,其中一些的数字位置是给定的,问最后排列的ans值最大是多少。ans的定义是所有连续两个数字的乘积的和。

思路:

完全排列复杂度n!,不可取。ans乘积的和具有最优子结构。按照tsp类似的方法设定状态dp[i][j]表示状态为i的情况下,最后选择第j个数可以得到的最优解。i状态的二进制位中为1的对应的数字排列在序列最前面。
其实这个状态信息量不小,每一个状态唯一不可知的是数字排列的顺序,但这可以在状态转移的过程中获得。状态转移方程是:dp[i|(1<<k)][k] = max(dp[i|(1<<k)][k],dp[i][j]+s[j]*s[k]); ans = max(dp[(1<<n)-1][k])(0<k<n),状态dp[s][j] 也可以看作是对于dp[i][j][s]的化简,其中i表示前i个位置的数,因为i可以由s二进制位中1的个数得出,还可以知道的是状态dp[s][j]一定向着状态dp[t][k]转移(t > s), 因此for循环的顺序可以确定了,即从小到大。注意:此题中非法状态较多,dp过程中要注意删除,且答案可能是负数,初始值的设定要小心。

代码:

#include <bits/stdc++.h>
using namespace std;
int s[20];
int p[20];
int dp[1<<16][20];
inline int counts(int st)
{
    int cn = 0;
    while(st>0)
    {
        if(st&1) cn++;
        st>>=1;
    }
    return cn;
}
void init()
{
    for(int i = 0; i < (1 <<16);i++)
        for(int j = 0; j < 20; j++)
            dp[i][j] = -2e9;
}
int main()
{
    int t,n;
    scanf("%d",&t);
    for(int ks = 1;ks <= t;ks++)
    {
        init();
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
            scanf("%d%d",s+i,p+i);
        for(int i = 0; i < n; i++)
            dp[1<<i][i] = 0;        
        for(int i = 1; i < (1<<n);i++)
        {
            for(int j = 0; j < n; j++)
            {
                if((i | (1<<j))!=i || (p[j] != -1 && counts(i) != p[j]+1))
                    continue;// 阻止非法状态向外转移
                for(int k = 0; k < n;k++)
                {
                    if((i | (1<<k)) == i) continue;
                    if(p[k]!= -1 && counts(i) != p[k]) continue;
                    int nst = i|(1<<k);
                    if(dp[nst][k] == -2e9)
                        dp[nst][k] = dp[i][j]+s[j]*s[k];
                    else
                        dp[nst][k] = max(dp[nst][k], dp[i][j]+s[j]*s[k]);
                }
            }
        }
        int ans = -2e9;
        for(int i = 0; i < n; i++)
            ans = max(ans,dp[(1<<n)-1][i]);
        printf("Case #%d:\n%d\n",ks,ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值