题目大意:
给出
Q
Q
组数据,
每组数据给出个点的带权无向图,点从
0
0
到标号,求起点
0
0
到起终点的最短
Hamilton
H
a
m
i
l
t
o
n
路径。
对于路径权值的计算:
1.经过所有点的权值相加。
2.经过的连续两个点的权值的乘积。
3.能够直接满足
a−>b−>c−>a
a
−
>
b
−
>
c
−
>
a
的连续三个点的乘积。
问最大权值和这个最大权值的路线有多少条。
1≤Q≤20
1
≤
Q
≤
20
N不超过13
N
不
超
过
13
分析:
不难想到可以壮压
dp
d
p
,
设
f[k][i][j]
f
[
k
]
[
i
]
[
j
]
表示状态为
k
k
,最后一个到达的点为,倒数第二个到达的点为
j
j
的最短路径。
num[k][i][j]
n
u
m
[
k
]
[
i
]
[
j
]
表示状态为
k
k
,最后一个到达的点为,倒数第二个到达的点为
j
j
时,路径大小为
f[k][i][j]
f
[
k
]
[
i
]
[
j
]
的数量。
则有
f[k][i][j]
f
[
k
]
[
i
]
[
j
]
=max{
f[k
f
[
k
xor
x
o
r
(1<<i)]
(
1
<<
i
)
]
[j][k]
[
j
]
[
k
]
+
a[i]
a
[
i
]
+
a[i]∗a[j]
a
[
i
]
∗
a
[
j
]
+
(flag[i][k]?a[i]∗a[j]∗a[k]:0)
(
f
l
a
g
[
i
]
[
k
]
?
a
[
i
]
∗
a
[
j
]
∗
a
[
k
]
:
0
)
}
flag[i][j]
f
l
a
g
[
i
]
[
j
]
表示
i,j
i
,
j
的连通关系,
对于
num[k][i][j]
n
u
m
[
k
]
[
i
]
[
j
]
,在计算
f[k][i][j]
f
[
k
]
[
i
]
[
j
]
的时候顺带计算一下即可。
最后找一个max{
f[2n−1][i][j]
f
[
2
n
−
1
]
[
i
]
[
j
]
},设为
x
x
,
统计所有的
num[2n−1][i][j]
n
u
m
[
2
n
−
1
]
[
i
]
[
j
]
的总和
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 14
using namespace std;
typedef long long ll;
ll num[1<<N][N][N], f[1<<N][N][N], a[N];
bool cp[N][N];
int main()
{
int T, n, m;
scanf("%d", &T);
while (T--)
{
memset(num, 0, sizeof(num));
memset(cp, 0, sizeof(cp));
memset(f, 0, sizeof(f));
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int u, v;
for (int i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
if (u != v)
{
cp[--u][--v] = cp[v][u] = 1;
int cnt = (1<<u)+(1<<v);
num[cnt][u][v] = num[cnt][v][u] = 1;
f[cnt][u][v] = f[cnt][v][u] = a[u] + a[v] + a[u]*a[v];
}
}
if (n == 1)
{
printf("%lld 1\n", a[0]);
continue;
}
for (int l = 0; l < (1<<n); l++)
for (int i = 0; i < n; i++)
if ((l>>i) & 1)
{
for (int j = 0; j < n; j++)
if (((l>>j) & 1) && i != j && cp[i][j])
{
for (int k = 0; k < n; k++)
if (((l>>k) & 1) && i != k && j != k && cp[j][k])
{
int cnt = l xor (1<<i);
if (!f[cnt][j][k]) continue;
ll tot = f[cnt][j][k] + a[i] + a[i]*a[j] + (cp[i][k]?a[i]*a[j]*a[k]:0);
if (tot > f[l][i][j])
{
f[l][i][j] = tot;
num[l][i][j] = num[cnt][j][k];
}
else if (tot == f[l][i][j])
{
num[l][i][j] += num[cnt][j][k];
}
}
}
}
ll ans = -1, tot = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i != j && cp[i][j])
{
if (f[(1<<n)-1][i][j] > ans)
{
ans = f[(1<<n)-1][i][j];
tot = num[(1<<n)-1][i][j];
}
else if (f[(1<<n)-1][i][j] == ans)
{
tot += num[(1<<n)-1][i][j];
}
}
if (ans == -1) printf("0 0\n");
else printf("%lld %lld\n", ans, tot/2);
}
return 0;
}