以下解題思路摘自:http://www.cnblogs.com/jackge/archive/2013/05/24/3096162.html
取dp[state][i][j]表示state狀態下倒數第二個島爲i,最後一個島爲j時的最優解,num[state][i][j]爲相應的路徑數目,其中state的二進制表示的i位爲1表示島i被訪問過,反之爲0。
則顯然當有邊(i,j)存在時,有如下初值可賦:
dp[(1<<i)+(1<<j)][i][j]=val[i]+val[j]+val[i]*val[j],num[(1<<i)+(1<<j)][i][j]=1。
如果狀態(state,i,j)可達,檢查島k,如果此時k沒有被訪問過並且有邊(j,k)存在,則做如下操作:
1)設tmp爲下一步訪問島k時獲得的總利益,r=state+(1<<k)。
2)如果tmp>dp[r][j][k],表示此時可以更新到更優解,則更新:
dp[r][j][k]=tmp,num[r][j][k]=num[state][i][j]。
3)如果tmp==dp[r][j][k],表示此時可以獲得達到局部最優解的更多方式,則更新:
num[r][j][k]+=num[state][i][j]。
最後檢查所有的狀態((1<<n)-1,i,j),疊加可以得到最優解的道路數。
需要注意的是,題目約定一條路徑的兩種行走方式算作一種,所以最終結果要除2。
以下代碼實現爲我自己寫的:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define max_inf 0x7f7f7f7f
#define min_inf 0x80808080
#define mod 1000000007
int n , m , dp[1<<13][13][13] , val[15];
int64 sum[1<<13][13][13];
bool Map[15][15];
void Solve()
{
int i , j , k , l , up = 1<<n , next;
memset(dp , -1 , sizeof dp);
memset(sum , 0 , sizeof sum);
//給相鄰點賦初值
for (i = 0 ; i < n ; i++)
{
for (j = 0 ; j < n ; j++)
{
if (i != j && Map[i][j])
{
dp[(1<<i)+(1<<j)][i][j] = val[i]+val[j]+val[i]*val[j];
sum[(1<<i)+(1<<j)][i][j] = 1;
}
}
}
for (i = 1 ; i < up ; i++)
{
for (j = 0 ; j < n ; j++)
{
if ( !(i & (1<<j)) )continue;
for (k = 0 ; k < n ; k++)
{
if (j == k)continue;
if ( !(i & (1<<k)) )continue;
if (dp[i][j][k] < 0)continue;
//找尋下一個可以連接的點
for (l = 0 ; l < n ; l++)
{
if (l == j || l == k)continue;
if (i & (1<<l))continue;
if (!Map[k][l])continue;
int tmp = dp[i][j][k]+val[l]+val[k]*val[l];
if (Map[j][l])tmp += val[j]*val[k]*val[l];
next = i+(1<<l);
if (dp[next][k][l] == tmp)
sum[next][k][l] += sum[i][j][k];
else if (dp[next][k][l] < tmp)
{
dp[next][k][l] = tmp;
sum[next][k][l] = sum[i][j][k];
}
}
}
}
}
int MAX = -1;
int64 ans = 0;
for (i = 0 ; i < n ; i++)
{
for (j = 0 ; j < n ; j++)
{
if (i == j)continue;
if (MAX < dp[up-1][i][j])
{
MAX = dp[up-1][i][j];
ans = sum[up-1][i][j];
}
else if (MAX == dp[up-1][i][j])
ans += sum[up-1][i][j];
}
}
if (MAX < 0)printf("0 0\n");
else printf("%d %I64d\n",MAX , ans/2);
}
int main()
{
int t , i;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
memset(Map , 0 , sizeof Map);
for (i = 0 ; i < n ; i++)scanf("%d",val+i);
for (i = 0 ; i < m ; i++)
{
int a , b;
scanf("%d%d",&a,&b);
a--,b--;
Map[a][b] = Map[b][a] = 1;
}
if (n == 1)
printf("%d 1\n",val[0]);
else
Solve();
}
return 0;
}