Islands and Bridges -- UVAlive 3267(状压DP)

[Submit]   [Go Back]   [Status]  

题目描述

 给你一个无向图,求一条权值和最大的曼哈顿回路(每个点有且仅有经过一次)。权分三部分

  1. 经过一个点,权值增加V【i】
  2. 经过一条边,权值增加V【i】*V【j】
  3. 如果连续两条边的三个端点在原图上两两连接,权值增加V【i】*V【j】*V【K】
因为点的个数最多有13个 所以容易想到状压DP。构建状态dp[s][i][j]表示在I点,由J点转移过来并且已经经过了点S{k}  其中S为二进制压缩表示。

那么在状态转移的时候,如果点J与待转移节点有边。那么增加V【i】*V【j】*V【to】,否则增加V【i】*V【j】。

注意节点个数为0的情况。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
#define maxn 100000
#define maxm 4000000
using namespace std;
struct Edge
{
    int from,to,next;
}es[maxm];
int cnt,p[maxm],n,m;
int dp[1<<15][15][15];
int num[1<<15][15][15];
int val[15];
void add(int x,int y)
{
    es[cnt].from = x;
    es[cnt].to = y;
    es[cnt].next = p[x];
    //cout<< x<<" "<<y<<"&&"<<endl;
    p[x] = cnt++;
}
int ma[15][15];
void init()
{
    memset(num,0,sizeof num);
    memset(dp,-1,sizeof dp);
    memset(ma,0,sizeof ma);
    memset(p,-1,sizeof p);
    cnt = 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int ks = 1;ks <= T;ks++)
    {
        scanf("%d %d",&n,&m);
        init();
        int mask = 1<<n;
        for(int i = 1;i <= n;i++) scanf("%d",&val[i]);
        for(int i = 1;i <= m;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            add(x,y);
            add(y,x);
            ma[x][y] = 1;
            ma[y][x] = 1;
        }
        if(n == 1)
        {
            printf("%d 1\n",val[1]);
            continue;
        }
        for(int i = 1;i <= n;i++)
            for(int j = p[i];j + 1;j = es[j].next)
            {
                int to = es[j].to;
                dp[(1<<(i-1))|(1<<(to-1))][to][i] = val[i] + val[to] + val[i]*val[to];
                num[(1<<(i-1))|(1<<(to-1))][to][i] = 1;
                //int kk = (1<<(i-1))|(1<<(to-1));
                //cout<< i<<" "<< to<<" "<< kk<<endl;
            }
        //dp[i][j][s] 起点 父 以走过的状态
       for(int s = 0;s < mask;s++)
        {
            for(int i = 1;i <= n;i++)//起点
            {
                for(int j = 1;j <= n;j++)//父节点
                {
                    if(dp[s][i][j] == -1) continue;
                    for(int k = p[i];k + 1;k = es[k].next)
                    {//cout<<"fds"<<endl;
                        int to = es[k].to;
                        int tmp;
                        if(s & (1<<(to-1))) continue;//保证每个点走一次
                        if(ma[j][to])
                            tmp = val[to] + val[to]*val[i] + val[to]*val[i]*val[j];
                        else
                            tmp = val[to] + val[to]*val[i];
                        if(dp[s|(1<< (to-1))][to][i] == dp[s][i][j] + tmp)
                        {
                            num[s|(1<< (to-1))][to][i] += num[s][i][j];
                        }
                        else if(dp[s|(1<< (to-1))][to][i] < dp[s][i][j] + tmp)
                        {
                            dp[s|(1<< (to-1))][to][i] = dp[s][i][j] + tmp;
                            num[s|(1<< (to-1))][to][i] = num[s][i][j];
                        }
                    }
                }
            }
        }
        //cout<<"fds"<<endl;
        long long ans = -1;
        long long ans2 = 0;
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= n;j++)
            {
                if(i == j) continue;
                //ans = max(ans,dp[(1<<n)-1][i][j]);

                if(dp[(1<<n)-1][i][j] == ans)
                    ans2 += num[(1<<n)-1][i][j];
                else if(dp[(1<<n)-1][i][j] > ans)
                {
                    ans = dp[(1<<n)-1][i][j];
                    ans2 = num[(1<<n)-1][i][j];
                }
            }
        if(ans == -1)
            ans = ans2 = 0;

        printf("%lld %lld\n",ans,ans2/2);
    }
    return 0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值