题目描述
给你一个无向图,求一条权值和最大的曼哈顿回路(每个点有且仅有经过一次)。权分三部分
- 经过一个点,权值增加V【i】
- 经过一条边,权值增加V【i】*V【j】
- 如果连续两条边的三个端点在原图上两两连接,权值增加V【i】*V【j】*V【K】
那么在状态转移的时候,如果点J与待转移节点有边。那么增加V【i】*V【j】*V【to】,否则增加V【i】*V【j】。
注意节点个数为0的情况。
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
#define maxn 100000
#define maxm 4000000
using namespace std;
struct Edge
{
int from,to,next;
}es[maxm];
int cnt,p[maxm],n,m;
int dp[1<<15][15][15];
int num[1<<15][15][15];
int val[15];
void add(int x,int y)
{
es[cnt].from = x;
es[cnt].to = y;
es[cnt].next = p[x];
//cout<< x<<" "<<y<<"&&"<<endl;
p[x] = cnt++;
}
int ma[15][15];
void init()
{
memset(num,0,sizeof num);
memset(dp,-1,sizeof dp);
memset(ma,0,sizeof ma);
memset(p,-1,sizeof p);
cnt = 0;
}
int main()
{
int T;
scanf("%d",&T);
for(int ks = 1;ks <= T;ks++)
{
scanf("%d %d",&n,&m);
init();
int mask = 1<<n;
for(int i = 1;i <= n;i++) scanf("%d",&val[i]);
for(int i = 1;i <= m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
add(x,y);
add(y,x);
ma[x][y] = 1;
ma[y][x] = 1;
}
if(n == 1)
{
printf("%d 1\n",val[1]);
continue;
}
for(int i = 1;i <= n;i++)
for(int j = p[i];j + 1;j = es[j].next)
{
int to = es[j].to;
dp[(1<<(i-1))|(1<<(to-1))][to][i] = val[i] + val[to] + val[i]*val[to];
num[(1<<(i-1))|(1<<(to-1))][to][i] = 1;
//int kk = (1<<(i-1))|(1<<(to-1));
//cout<< i<<" "<< to<<" "<< kk<<endl;
}
//dp[i][j][s] 起点 父 以走过的状态
for(int s = 0;s < mask;s++)
{
for(int i = 1;i <= n;i++)//起点
{
for(int j = 1;j <= n;j++)//父节点
{
if(dp[s][i][j] == -1) continue;
for(int k = p[i];k + 1;k = es[k].next)
{//cout<<"fds"<<endl;
int to = es[k].to;
int tmp;
if(s & (1<<(to-1))) continue;//保证每个点走一次
if(ma[j][to])
tmp = val[to] + val[to]*val[i] + val[to]*val[i]*val[j];
else
tmp = val[to] + val[to]*val[i];
if(dp[s|(1<< (to-1))][to][i] == dp[s][i][j] + tmp)
{
num[s|(1<< (to-1))][to][i] += num[s][i][j];
}
else if(dp[s|(1<< (to-1))][to][i] < dp[s][i][j] + tmp)
{
dp[s|(1<< (to-1))][to][i] = dp[s][i][j] + tmp;
num[s|(1<< (to-1))][to][i] = num[s][i][j];
}
}
}
}
}
//cout<<"fds"<<endl;
long long ans = -1;
long long ans2 = 0;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
{
if(i == j) continue;
//ans = max(ans,dp[(1<<n)-1][i][j]);
if(dp[(1<<n)-1][i][j] == ans)
ans2 += num[(1<<n)-1][i][j];
else if(dp[(1<<n)-1][i][j] > ans)
{
ans = dp[(1<<n)-1][i][j];
ans2 = num[(1<<n)-1][i][j];
}
}
if(ans == -1)
ans = ans2 = 0;
printf("%lld %lld\n",ans,ans2/2);
}
return 0;
}