传送门: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;
}