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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章