【状压dp】【POJ2288】Islands and Bridges【Hamilton路】

题意:

      给你一张图,找到一条Hamilton路,要求从其中一个节点出发,遍历图中所有节点一遍。

      这样一条路径的权值计算为三部分的累加。

      第一部分:这条路径上所有节点的权值之和。

      第二部分:这条路径上相邻两点的乘积。

      第三部分:这条路径上相邻三个点的乘积。(如果三个点互相直接相连)

      如,a1、a2、a3、a4这四个点形成的一条路径,第一部分为w1+w2+w3+w4,第二部分为w1*w2+w2*w3+w3*w4,第三部分为如果a1和a3直接相连,a2和a4直接相连,则为a1*a2*a3+a2*a3*a4。

      要求输出按照上述计算方法所能找到的最大的Hamilton路,并且输出这条路的个数,n <= 13。

思路:

      因为这样一条路径的更新涉及到最后两个点,所以三维dp肯定要记录最后两个节点了,然后第一维用状态压缩记录当前走过的节点,第二维记录这条路径最后一个节点,第三维记录倒数第二个节点。

      dp[i][j][k]表示当前状态为i,最后一个节点为j,倒数第二个节点为k。

      然后dp[i][j][k]去更新dp[i|(1<<p)][p][j],四个for循环结束本题。

      本题细节较多,比如num会爆int,可能会出现0 x这样的答案等等,详见代码。

总结:

      此类状压dp,大都较为相似,先开一维记录状态,再根据题意,看需要记录一个数值,多记录一个数值则需要多开一维状态。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
typedef long long ll;

int n,m;
int a[20];
int w[20][20];
int dp[1<<13][15][15];
ll way[1<<13][15][15];

void solve()
{
    memset(dp,-1,sizeof dp);
    rep(i,0,n-1)
        rep(j,0,n-1)
            if(w[i][j])
                dp[(1<<i)|(1<<j)][i][j] = a[i]+a[j]+a[i]*a[j],
                way[(1<<i)|(1<<j)][i][j] = 1;
    rep(i,0,(1<<n)-1)
        rep(j,0,n-1)  //最后一个
            if(i&(1<<j))
                rep(k,0,n-1)  //倒数第二个
                    if((i&(1<<k)) && w[j][k] && dp[i][j][k] != -1){
                    //  cout << "$$$$" << endl;
                        rep(h,0,n-1)//倒数第三个
                            if(!(i&(1<<h)) && w[h][j])
                            {
                                int ans = dp[i][j][k]+a[j]*a[h]+a[h];
                                if(w[h][k]) ans += a[k]*a[h]*a[j];  
                                if(ans == dp[i^(1<<h)][h][j]) way[i^(1<<h)][h][j]+=way[i][j][k];
                                else if(ans > dp[i^(1<<h)][h][j]){
                                    dp[i^(1<<h)][h][j] = ans;
                                    way[i^(1<<h)][h][j] = way[i][j][k];
                                }                           
                            }
                    }
    int maxn = 0;
    ll num = 0;
    rep(i,0,n-1)
        rep(j,0,n-1)
            if(w[i][j]){
                if(maxn < dp[(1<<n)-1][i][j])
                    maxn = dp[(1<<n)-1][i][j], num = way[(1<<n)-1][i][j];
                else if(maxn == dp[(1<<n)-1][i][j]) num += way[(1<<n)-1][i][j];
            }
    printf("%d %lld\n",maxn,num/2);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(w,0,sizeof w);
        memset(way,0,sizeof way);
        scanf("%d%d",&n,&m);
        rep(i,0,n-1) scanf("%d",&a[i]);
        if(n == 1) {
            printf("%d 1\n", a[0]);
            continue ;
        }
        rep(i,1,m)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x--, y--;
            w[x][y] = 1, w[y][x] = 1;
        }
        solve();
    }
    return 0;
}

/*
dp[1<<13][15][15]:last next
*/

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/83341736
今日推荐