【汉密尔顿、DP|状态压缩】POJ-2288 Islands and Bridges

Islands and Bridges

Time Limit: 4000MS   Memory Limit: 65536K
     

Description

Given a map of islands and bridges that connect these islands, a Hamilton path, as we all know, is a path along the bridges such that it visits each island exactly once. On our map, there is also a positive integer value associated with each island. We call
a Hamilton path the best triangular Hamilton path if it maximizes the value described below.

Suppose there are n islands. The value of a Hamilton path C1C2...Cn is calculated as the sum of three parts. Let Vi be the value for the island Ci. As the first part, we sum over all the Vi values for each island in the path. For the second part, for each edge
CiCi+1 in the path, we add the product Vi*Vi+1. And for the third part, whenever three consecutive islands CiCi+1Ci+2 in the path forms a triangle in the map, i.e. there is a bridge between Ci and Ci+2,
we add the product Vi*Vi+1*Vi+2.

Most likely but not necessarily, the best triangular Hamilton path you are going to find contains many triangles. It is quite possible that there might be more than one best triangular Hamilton paths; your second task is to find the number of such paths.

Input

The input file starts with a number q (q<=20) on the first line, which is the number of test cases. Each test case starts with a line with two integers n and m, which are the number of islands and the number of bridges in the map, respectively. The next line
contains n positive integers, the i-th number being the Vi value of island i. Each value is no more than 100. The following m lines are in the form x y, which indicates there is a (two way) bridge between island x and island y. Islands are numbered from 1
to n. You may assume there will be no more than 13 islands.

Output

For each test case, output a line with two numbers, separated by a space. The first number is the maximum value of a best triangular Hamilton path; the second number should be the number of different best triangular Hamilton paths. If the test case does not
contain a Hamilton path, the output must be `0 0‘.

Note: A path may be written down in the reversed order. We still think it is the same path.

Sample Input

2
3 3
2 2 2
1 2
2 3
3 1
4 6
1 2 3 4
1 2
1 3
1 4
2 3
2 4
3 4

Sample Output

22 3
69 1

————————————————————————————————————————————————————————————————————————————————————————————————

题意:给出n个点,m条边。每个点有一个权值w。找出一条汉密尔顿路径,使它的值最大。一条汉密尔顿路径的值由三部分组成:

1) 路径上每个点的权值之和

2) 路径上每条边u-v,将其权值的积累加起来。即w[u]*w[v]

3) 如果三个点形成一个三角形,例如i、i+1、i+2,那么将w[i]*w[i+1]*w[i+2]累加起来

一条汉密尔顿路径可能包含多个三角形,一张图中也可能包含多个最好的汉密尔顿路径。输出最大的汉密尔顿路径的值,以及这样的汉密尔顿路径的个数。同一条汉密尔顿路径的两种走法算作一种。

参考:Jack Ge for ACM

思路:汉密尔顿:经过每个点一次且仅一次。

汉密尔顿回路问题是旅行商问题。但是别想多了,这种NP完全问题大数据无解。而且这不是回路,是路径。

注意数据范围,13。这就是摆明了让你进行状态压缩DP的。

状态像这样描述:

dp[p][i][j](dp[1<<13][13][13]): p代表当前已经拜访的点的状态,j是前一个点,i是前一个点的前一个点。

ways[p][i][j]: 储存当前最好的汉密尔顿路径的条数。

我们的目标是dp[1<<13-1][x][y],因此也不需要判断是否存在汉密尔顿路径(况且至今没找到等价条件)

首先是初始化(在判断三角形之前 我们可以这样初始化):

dp[1<<i|1<<j][i][j] = w[i] + w[j] + w[i] * w[j]

ways[1<<i|1<<j][i][j] = 1

状态转移:

状态dp[p][i][j]可达之后,对于下一个点k:

dp[p|1<<k][j][k] = max{dp[p|1<<k][j][k], dp[p][i][j] + w[k] + w[k]*w[j] (+ W[三角形])}

P.S. 因为最好的汉密尔顿路径数目可能相当大,超出int所以使用long long来保存ways

代码如下:

/*
ID: j.sure.1
PROG:
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <iostream>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
/****************************************/
int n, m;
const int N = 13;
bool G[N][N];
int w[N];
int dp[1<<N][N][N];
LL ways[1<<N][N][N];

void init()
{
    memset(G, 0, sizeof(G));
    memset(dp, -1, sizeof(dp));
    memset(ways, 0, sizeof(ways));
}

void DP()
{
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) if(G[i][j]) {
            dp[(1<<i)|(1<<j)][i][j] = w[i] + w[j] + w[i]*w[j];
            ways[(1<<i)|(1<<j)][i][j] = 1;
        }
    }
    for(int p = 0; p < (1<<n); p++) {
        for(int i = 0; i < n; i++) if(p & (1<<i)) {
            for(int j = 0; j < n; j++) if(p & (1<<j)) {
                if(G[i][j] && dp[p][i][j] != -1) {
                    for(int k = 0; k < n; k++) {
                        if(G[j][k] && k != i && !(p & (1<<k))) {
                            int tmp = dp[p][i][j] + w[k] + w[k] * w[j];
                            if(G[i][k]) {
                                tmp += w[i] * w[j] * w[k];
                            }//形成三角形
                            if(dp[p|(1<<k)][j][k] < tmp) {
                                dp[p|(1<<k)][j][k] = tmp;
                                ways[p|(1<<k)][j][k] = ways[p][i][j];
                            }//决定是否更改原方案
                            else if(dp[p|(1<<k)][j][k] == tmp) {
                                ways[p|(1<<k)][j][k] += ways[p][i][j];
                            }
                        }
                    }
                }
            }
        }
    }
}

int main()
{
#ifdef J_Sure
//  freopen("000.in", "r", stdin);
//  freopen(".out", "w", stdout);
#endif
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        init();
        for(int i = 0; i < n; i++) {
            scanf("%d", &w[i]);
        }
        if(n == 1) {
            printf("%d 1\n", w[0]);
            continue ;
        }
        int u, v;
        for(int i = 0; i < m; i++) {
            scanf("%d%d", &u, &v);
            u--; v--;
            G[v][u] = G[u][v] = true;
        }
        DP();
        int maxi = 0;
        LL ans = 0;
        int P = (1<<n) - 1;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) if(G[i][j]) {
                if(maxi < dp[P][i][j]) {
                    maxi = dp[P][i][j];
                    ans = ways[P][i][j];
                }
                else if(maxi == dp[P][i][j]) {
                    ans += ways[P][i][j];
                }
            }
        }
        printf("%d %lld\n", maxi, ans / 2);
    }
    return 0;
}
时间: 2024-10-14 21:52:00

【汉密尔顿、DP|状态压缩】POJ-2288 Islands and Bridges的相关文章

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 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)

Language: Default Islands and Bridges Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 9312   Accepted: 2424 Description Given a map of islands and bridges that connect these islands, a Hamilton path, as we all know, is a path along the b

【以前的空间】poj 2288 Islands and Bridges

一个不错的题解 : http://blog.csdn.net/accry/article/details/6607703 这是一道状态压缩.每个点有一个值,我们最后要求一个最值sum.sum由三部分组成:①每个点的值②每个点与他相邻的点的乘积③如果存在三个点成环,还要加上这三个点的值的乘积. 状态转移方程为:dp[i][j][k]=max(dp[i,j,k],dp[i'][k][l]+temp) j表示当前点,k表示上一个点,l表示上上一个点. 其中i,i'表示可以走到i点的状态,temp表示这

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

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&

poj 2288 Islands and Bridges_状态压缩dp_哈密尔顿回路问题

题目链接 题目描写叙述:哈密尔顿路问题.n个点,每个点有权值,设哈密尔顿路为 C1C2...Cn,Ci的权值为Vi,一条哈密尔顿路的值分为三部分计算: 1.每个点的权值之和2.对于图中的每一条CiCi+1,加上Vi*Vi+1 3.对于路径中的连续三个点:CiCi+1Ci+2,若在图中,三点构成三角形,则要加上Vi*Vi+1*Vi+2 求一条汉密尔顿路能够获得的最大值,而且还要输出有多少条这种哈密尔顿路. 这道题的状态感觉不是非常难想,由于依据一般的哈密尔顿路问题,首先想到的是设计二维状态,dp[

dp状态压缩

dp状态压缩 动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的就是那种状态很多,不容易用一般的方法表示的动态规划问题,这个就更加的难于把握了.难点在于以下几个方面:状态怎么压缩?压缩后怎么表示?怎么转移?是否具有最优子结构?是否满足后效性?涉及到一些位运算的操作,虽然比较抽象,但本质还是动态规划.找准动态规划几个方面的问题,深刻理解动态规划的原理,开动脑筋思考问题.这才是掌握动态规划的关键. 动态规划最关键的要处理的问题就是位运算的操作,容易出错,状态的设计也直