傳送門:http://acm.hdu.edu.cn/showproblem.php?pid=5691
分析:看到N的範圍,八成就是狀壓。q[i]表示第i個位置填第幾個數。
狀態:dp[i][j]表示用了i這種情況裏這些數,從左往右填以j爲結尾的最大值。
狀態轉移方程:當第k個位置已經被固定填什麼數時,向狀態st裏添加i並以i爲結尾,dp[st|(1<<q[k])][q[k]] = max(dp[st|(1<<q[k])][q[k]], dp[st][j]+a[j]*a[q[k]])。當第k位置不固定,枚舉各個數,該數不在集合時,便添加到集合裏,dp[st|(1<<i)][i] = max(dp[st|(1<<i)][i], dp[st][j]+a[j]*a[i])。
代碼:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 20;
const ll INF = 1LL<<50;
ll dp[1<<N][N];
int p[N], q[N], a[N];
int main() {
int T, n;
scanf("%d", &T);
for(int kase = 1; kase <= T; kase++) {
scanf("%d", &n);
memset(q, -1, sizeof(q));
for(int i = 0; i < n; i++) {
scanf("%d%d", &a[i], &p[i]);
if(p[i] >= 0) q[p[i]] = i;
}
for(int i = 0; i < (1<<n); i++)
for(int j = 0; j < n; j++)
dp[i][j] = -INF;
if(~q[0]) dp[1<<q[0]][q[0]] = 0;
else {
for(int i = 0; i < n; i++)
dp[1<<i][i] = 0;
}
for(int st = 1; st < (1<<n); st++) {
int k = 0;
for(int i = 0; i < n; i++)
k += st>>i&1;
if(~q[k]) {
if(st & (1<<q[k])) continue;
for(int j = 0; j < n; j++) {
if(~st&(1<<j)) continue;
dp[st|(1<<q[k])][q[k]] = max(dp[st|(1<<q[k])][q[k]], dp[st][j]+a[j]*a[q[k]]);
}
}
else {
for(int i = 0; i < n; i++) {
if(st & (1<<i)) continue;
for(int j = 0; j < n; j++) {
if(~st&(1<<j)) continue;
dp[st|(1<<i)][i] = max(dp[st|(1<<i)][i], dp[st][j]+a[j]*a[i]);
}
}
}
}
ll ans = -INF;
for(int i = 0; i < n; i++)
ans = max(ans, dp[(1<<n)-1][i]);
printf("Case #%d:\n%I64d\n", kase, ans);
}
return 0;
}