POJ 2288 Islands and Bridges(状压DP)题解

题意:n个点,m有向边,w[i]表示i的价值,求价值最大的哈密顿图(只经过所有点一次)。价值为:所有点的w之和,加上,每条边的价值 = w[i] * w[j],加上,如果连续的三个点相互连接的价值 = w[i] * w[j] * w[k]。n <= 13。

思路:dp[state][i][j]表示state状态下,最后两个为i,j。

代码:

#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-8;
const int maxn = 15 + 10;
const int M = maxn * 30;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
const int MOD = 1e4 + 7;
int w[maxn];
int n, m;
int g[maxn][maxn];
int dp[(1 << 13) + 10][maxn][maxn];
ll way[(1 << 13) + 10][maxn][maxn];
void solve(){
    memset(dp, -1, sizeof(dp));
    memset(way, 0, sizeof(way));
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            if(i == j) continue;
            if(g[i][j] == INF) continue;
            dp[(1 << i) | (1 << j)][i][j] = w[i] + w[j] + g[i][j];
            way[(1 << i) | (1 << j)][i][j]++;
        }
    }
    for(int t = 0; t < (1 << n) - 1; t++){
        for(int i = 0; i < n; i++){
            if(!((1 << i) & t)) continue;
            for(int j = 0; j < n; j++){
                if(!((1 << j) & t)) continue;
                if(g[i][j] == INF) continue;
                if(dp[t][i][j] == -1) continue;
                for(int k = 0; k < n; k++){
                    if((1 << k) & t) continue;
                    if(g[j][k] == INF) continue;
                    ll ret = dp[t][i][j] + w[k] + g[j][k];
                    if(g[i][k] != INF) ret += w[i] * w[j] * w[k];
                    if(dp[(1 << k) | t][j][k] < ret){
                        dp[(1 << k) | t][j][k] = ret;
                        way[(1 << k) | t][j][k] = way[t][i][j];
                    }
                    else if(dp[(1 << k) | t][j][k] == ret){
                        way[(1 << k) | t][j][k] += way[t][i][j];
                    }
                }
            }
        }
    }
}
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i++)
            scanf("%d", &w[i]);
        memset(g, INF, sizeof(g));
        for(int i = 0; i < m; i++){
            int u, v;
            scanf("%d%d", &u, &v);
            u--, v--;
            g[u][v] = g[v][u] = w[u] * w[v];
        }
        if(n == 1){
            printf("%d 1\n", w[0]);
            continue;
        }
        solve();
        ll ans = 0, num = 0;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(i == j) continue;
                if(g[i][j] == INF) continue;
                if(dp[(1 << n) - 1][i][j] > ans){
                    ans = dp[(1 << n) - 1][i][j];
                    num = way[(1 << n) - 1][i][j];
                }
                else if(dp[(1 << n) - 1][i][j] == ans){
                    num += way[(1 << n) - 1][i][j];
                }
            }
        }
        printf("%lld %lld\n", ans, num / 2);
    }
    return 0;
}
/*
3
3 1
2 2 2
1 2
*/

原文地址:https://www.cnblogs.com/KirinSB/p/10945242.html

时间: 2024-10-13 20:01:25

POJ 2288 Islands and Bridges(状压DP)题解的相关文章

poj 2288 Islands and Bridges ——状压DP

题目:http://poj.org/problem?id=2288 状压挺明显的: 一开始写了(记忆化)搜索,但一直T: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int const inf=0x3f3f3f3f; int T,n,m,v[15]; ll ans,cnt

poj2288(Islands and Bridges) 状压DP

题目链接:http://poj.org/problem?id=2288 题意:每个点有一个权值Vi,找一条哈密顿路径,路径的权值来自三条:1 路径上的Vi之和 2 所有相邻点对ij的Vi*Vj之和 3 相邻连续三点i,j,k(并且三点要构成三角形)Vi*Vj*Vk之和. 解法:dp[st][i][j]表示从j走到i并且剩下集合st没有走的最大权值.关于路径书,在转移的时候顺便计算即可:这道题令自己恶心了好久,最后原因是自己犯了一个严重错误,题目读错了,没有读到Vi*Vj*Vk要保证ijk能够构成

POJ 2288 Islands And Bridges 状态压缩dp+哈密顿回路

题意:n个点 m条边的图,路径价值定义为相邻点乘积,若路路径c[i-1]c[i]c[i+1]中c[i-1]-c[i+1]有边 则价值加上三点乘积找到价值最大的哈密顿回路,和相应的方法数n<=13.暴力dfs O(13!) TLE 由于n<13 经典的状态压缩dp [状态] [当前点位置 ][前一点位置] 注意上一个状态必须合法才能进行转移设状态dp[s][i][j] 当前状态为s,当前点为i上一个点为j的最大价值, ans=max(dp[(1<<n)-1][i])dp[s][i][

POJ 2288 Islands and Bridges 哈密尔顿路 状态压缩DP

找最长的其实是很裸的状态压缩DP,棘手的地方是要统计数量,其实只要再来一个数组存就好. 不过代码比较长,细节要注意的地方毕较多,wa了很多发,还是要仔细啊 用递推和记忆化搜索分别写了一遍 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <

poj 2288 Islands and Bridges

题意: 给你一个双向连通图,求 获得权值最大 的 哈密顿通路的 权值 和 这个权值对应的数目: 其中权值计算方法是  列如 ABCD  权值是a+b+c+d+ab+bc+cd 如果 A,B,C  和B,C,D 可构成三角形分别加上abc,bcd: 这个题 和poj 3311  很相像: 那个需要记录一个最后到达的地方   这个需要记录俩个罢了 DP[i][a][b]其中 i  二进制 中1表示这个点走过了   最后走的的 的是b>>a 因为对于已经走过了{1,2,3,4,,5,6,..,N}

POJ 2411 Mondriaan&#39;s Dream(状压DP)

http://poj.org/problem?id=2411 求一个n*m矩阵用1*2方块去填满的情况有几种 思路:状压dp,先预处理那些状态之间能互相到达,情况就几种,上一个两个1,下一个状态也两个1,上一个为0,下一个必须为1,还有一种是上一个为1,下一个为0的情况 然后就一层层往后递推即可 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; int

POJ 2411 Mondriaan&#39;s Dream ——状压DP 插头DP

[题目分析] 用1*2的牌铺满n*m的格子. 刚开始用到动规想写一个n*m*2^m,写了半天才知道会有重复的情况. So Sad. 然后想到数据范围这么小,爆搜好了.于是把每一种状态对应的转移都搜了出来. 加了点优(gou)化(pi),然后poj上1244ms垫底. 大概的方法就是考虑每一层横着放的情况,剩下的必须竖起来的情况到下一层取反即可. 然后看了 <插头DP-从入门到跳楼> 这篇博客,怒抄插头DP 然后16ms了,自己慢慢YY了一下,写出了风(gou)流(pi)倜(bu)傥(tong)

poj 2411 Mondriaan&#39;s Dream 状压dp入门

题意: 求h*w的矩形被1*2的小矩形覆盖的方案数. 分析: 状压dp入门,<挑战程序设计竞赛>上讲的很好,好几天才看懂. 代码: #include <iostream> using namespace std; __int64 ans[16][16]; int n,m; __int64 dp[2][1<<16]; __int64 solve() { int i,j,used; memset(dp,0,sizeof(dp)); __int64 *crt=dp[0],*n

POJ 2411 Mondriaan&#39;s Dream (状压DP)

题意:给出一个n*m的棋盘,及一个小的矩形1*2,问用这个小的矩形将这个大的棋盘覆盖有多少种方法. 析:对第(i,j)位置,要么不放,要么竖着放,要么横着放,如果竖着放,我们记第 (i,j)位置为0,(i+1,j)为1,如果横着放,那么我们记 (i,j),(i,j+1)都为1,然后dp[i][s]表示 第 i 行状态为 s 时,有多少方法,那么我们就可以考虑与和匹配的状态,这个也很容易判断. 代码如下: #pragma comment(linker, "/STACK:1024000000,102

POJ 1185 炮兵阵地(状压DP入门)

http://poj.org/problem?id=1185 状压DP: 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int dp[105][100][100]; 7 int ma[105],st[105]; 8 9 int ok(int x) 10 { 11 return (x&(x<<1))+(x&(x&