1. 程式人生 > >【漢密爾頓、DP|狀態壓縮】POJ-2288 Islands and Bridges

【漢密爾頓、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]累加起來 一條漢密爾頓路徑可能包含多個三角形,一張圖中也可能包含多個最好的漢密爾頓路徑。輸出最大的漢密爾頓路徑的值,以及這樣的漢密爾頓路徑的個數。同一條漢密爾頓路徑的兩種走法算作一種。 思路:漢密爾頓:經過每個點一次且僅一次。 漢密爾頓迴路問題是旅行商問題。但是別想多了,這種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;
}