题目大意:
给出
组数据,
每组数据给出
个点的带权无向图,点从
到
标号,求起点
到起终点
的最短
路径。
对于路径权值的计算:
1.经过所有点的权值相加。
2.经过的连续两个点的权值的乘积。
3.能够直接满足
的连续三个点的乘积。
问最大权值和这个最大权值的路线有多少条。
分析:
不难想到可以壮压
,
设
表示状态为
,最后一个到达的点为
,倒数第二个到达的点为
的最短
路径。
表示状态为
,最后一个到达的点为
,倒数第二个到达的点为
时,
路径大小为
的数量。
则有
=max{
+
+
+
}
表示
的连通关系,
对于
,在计算
的时候顺带计算一下即可。
最后找一个max{
},设为
,
统计所有
的
的总和
代码:
#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;
}