【题意】每个点有一个权值,求得分最大的路径的得分和这样的路径条数。得分规则如下
Suppose there are n islands. The value of a Hamilton path C1C2...Cn is calculated as the sum of three parts. Let Vi be the value for the island Ci. As the first part, we sum over all the Vi values for each island in the path. For the second part, for each edge CiC i+1 in the path, we add the product Vi*V i+1. And for the third part, whenever three consecutive islands CiC i+1C i+2 in the path forms a triangle in the map, i.e. there is a bridge between Ci and C i+2, we add the product Vi*V i+1*V i+2.
【解析】每个点的状态有前两个点决定,所以用dp[s][a][b]表示通过b到达a时的最大得分,可以有dp[s`][b][c]计算得到,s`=s^(1<<(a-1))。
【状态转移】dp[s][a][b] = max{ dp[s`][b][c] + temp }, temp = val[a] + val[a]*val[b], 如果a、c之间有边,要加上val[a]*val[b]*val[c]。
【注意】存储路径条数要用long long,因为极限数据的路径条数为13! = 3113510400。
AC代码如下:
#include "iostream"
#include "cstdio"
#include "cmath"
#include "cstring"
using namespace std;
typedef long long ll;
int val[14], dis[14][14];
ll pre[8192][14][14];
ll dp[8192][14][14];
int main()
{
int q, n, m, x, y;
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&n,&m);
memset(dis,0,sizeof(dis));
for(int i = 1; i <= n; i++) scanf("%d",&val[i]);
for(int i = 0; i < m; i++)
{
scanf("%d%d",&x,&y);
dis[x][y] = dis[y][x] = 1;
}
ll ans = -1, num = 0;
if(n==0) { printf("0 0\n"); continue;}
if(n==1)
{
printf("%d 1\n",val[1]);
continue;
}
memset(dp,-1,sizeof(dp));
memset(pre,0,sizeof(pre));
int ns = (1<<n)-1;
for(int s = 1; s <= ns; s++)
{
for(int a = 1; a <= n; a++)
{
if((s&(1<<(a-1)))==0) continue;
for(int b = 1; b <= n; b++)
{
if(a==b || !dis[a][b] || (s&(1<<(b-1)))==0) continue;
if(s == (1<<(a-1))+(1<<(b-1)))
{
dp[s][a][b] = val[a]+val[b]+val[a]*val[b];
pre[s][a][b] = 1;
continue;
}
int ts;
ll td;
for(int c = 1; c <= n; c++)
{
if(c==a || c==b || !dis[b][c] || (s&(1<<(c-1)))==0) continue;
ts = s^(1<<(a-1));
if(dp[ts][b][c] == -1 || pre[ts][b][c]==0) continue;
td = dp[ts][b][c] + val[a] + val[a]*val[b];
if(dis[a][c]) td += val[a]*val[b]*val[c];
if(td > dp[s][a][b])
{
dp[s][a][b] = td;
pre[s][a][b] = pre[ts][b][c];
}
else if(td == dp[s][a][b]) pre[s][a][b] += pre[ts][b][c];
}
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(i==j || !dis[i][j] || dp[ns][i][j]==-1 || pre[ns][i][j]==0) continue;
if(dp[ns][i][j] > ans)
{
ans = dp[ns][i][j];
num = pre[ns][i][j];
}
else if(dp[ns][i][j] == ans) num += pre[ns][i][j];
}
}
if(ans == -1) printf("0 0\n");
else printf("%I64d %I64d\n",ans,num/2);
}
return 0;
}