题目链接:http://poj.org/problem?id=2288
题目描述:哈密尔顿路问题。n个点,每一个点有权值,设哈密尔顿路为 C1C2...Cn,Ci的权值为Vi,一条哈密尔顿路的值分为三部分计算:
1.每一个点的权值之和
2.对于图中的每一条CiCi+1,加上Vi*Vi+1
3.对于路径中的连续三个点:CiCi+1Ci+2,若在图中,三点构成三角形,则要加上Vi*Vi+1*Vi+2
求一条汉密尔顿路可以获得的最大值,并且还要输出有多少条这样的哈密尔顿路。
这道题的状态感觉不是很难想,因为根据一般的哈密尔顿路问题,首先想到的是设计二维状态,dp[i , s]表示当前在i点,走过的点形成状态集合s。但是这道题在求解值的时候有一个不一样的地方,就是第三部分,如果还是设计成二维的状态,就会很麻烦,因为每加入一个新点,要判断新点、当前点、倒数第二个点是否构成三角形,所以要记录倒数第二个点。很自然地想到扩展状态的维数,增加一维,记录倒数第二个点。
1> 设计状态:
dp[i , j , s]表示当前站在j点,前一个点是i点,形成的状态集合是s,此时的最大值,way[i , j , s]记录当前状态下达到最大值的路径数;
2> 状态转移:
设k点不在集合s中,且存在边<j , k>
设q为下步到达k点获得的最大值
令r = s + (1<<k),为当前站在点k,前一个点为j,形成状态集合r
若i,j,k形成三角形,则q = dp[i][j][s] + v[k] + v[j]*v[k] + v[i]*v[j]*v[k];
否则,q = dp[i][j][s] + v[k] + v[j]*v[k];
若q大于dp[j][k][r];则:
dp[j][k][r] = q
way[j][k][r] = way[i][j][s];
若q等于dp[j][k][r],则:
way[j][k][r] += way[i][j][s];
3> 初始化:
显然,若i点到j点有边,则:
dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
way[i][j][(1<<i)+(1<<j)] = 1;
4> 结果的产生:
最后的结果我们要枚举点i和j,找到最大的dp[i][j][(1<<n)-1],并且更新记录路径数ansp,最后ansp要除2才是结果,因为题目最后一句话,正向反向是一样的路。
此外,需要注意的是discuss提到的特殊情况,要用__int64,并且注意n等于1时,最大值就是第一个点的权值,路径数为1。
(ps:在处理特殊情况时,忘记换行,还PE一次,这年头PE的还真少见啊。。。)
代码如下,祝1Y。
#include <stdio.h>
#include <string.h>
typedef __int64 i64;
const int maxn = 13;
const int maxs = 1<<maxn | 1;
i64 dp[maxn][maxn][maxs], way[maxn][maxn][maxs];
int map[maxn][maxn], v[maxn];
int n, m, S;
void StateCompressDp() {
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 (map[i][j]) {
dp[i][j][(1<<i)+(1<<j)] = v[i] + v[j] + v[i]*v[j];
way[i][j][(1<<i)+(1<<j)] = 1;
}
}
}
for (int p = 3; p < S; p++) {
for (int i = 0; i < n; i++) {
if (!(p&1<<i)) continue;
for (int j = 0; j < n; j++) {
if (i == j || !(p&1<<j) || dp[i][j][p] == -1) continue;
for (int k = 0; k < n; k++) {
if (p&1<<k || !map[j][k]) continue;
int r = p + (1 << k);
i64 q = dp[i][j][p] + v[k] + v[j]*v[k];
if (map[i][k]) {
q += v[i] * v[j] * v[k];
}
if (q > dp[j][k][r]) {
dp[j][k][r] = q;
way[j][k][r] = way[i][j][p];
} else if (q == dp[j][k][r]) {
way[j][k][r] += way[i][j][p];
}
}
}
}
}
return ;
}
int main () {
int t;
scanf("%d", &t);
while (t--) {
int tmpx, tmpy;
memset(map, 0, sizeof(map));
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d", &v[i]);
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &tmpx, &tmpy);
map[tmpx-1][tmpy-1] = map[tmpy-1][tmpx-1] = 1;
}
S = 1 << n;
if (n == 1) {
printf("%d %d\n", v[0], 1);
} else {
StateCompressDp();
i64 ansv = -1, ansp = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) continue;
if (dp[i][j][S-1] > ansv) {
ansv = dp[i][j][S-1];
ansp = way[i][j][S-1];
} else if (dp[i][j][S-1] == ansv) {
ansp += way[i][j][S-1];
}
}
}
printf("%I64d %I64d\n", ansv == -1 ? 0 : ansv, ansp/2);
}
}
return 0;
}