一个有500个节点的无向图,有至多15个节点上有宝藏,每次进入每个点的时候都有对应的花费,过某条路也有对应的花费。求从s到t,经过所有的宝藏点,的最小花费。
首先从s和每个宝藏点出发,共计求16次最短路。然后把原图缩小,只留s,t和宝藏点。这样就变成了求17个点的,从s到t的,经过所有其他点的最短路。直接状态压缩,然后用dijkstra求即可。
#include <cstdio>
#include <queue>
using namespace std;
inline bool update(int &a,int b) {
if (a==-1||a>b) {
a=b;
return true;
}
return false;
}
bool haveTreasure[500];
int treasureIn[15];
int cost[500];
int edg[500][500];
int dp[17][32768];
int cost2[17][17];
int treasureN;
struct HeapNode {
int i,v;
HeapNode (int ii,int vv) {
i=ii;v=vv;
}
friend bool operator < (const HeapNode &a,const HeapNode &b) {
return a.v>b.v;
}
};
void calCost(int s,int tar,int n,int ss,int tt) {
static int v[500];
static priority_queue<HeapNode> c;
for (int i=0;i<n;i++) v[i]=-1;
while (!c.empty()) c.pop();
v[s]=0;
c.push(HeapNode(s,0));
while (!c.empty()) {
int i=c.top().i,curv=c.top().v;
c.pop();
if (curv==v[i]) {
for (int j=0;j<n;j++) {
if (edg[i][j]!=-1) {
if (update(v[j],curv+edg[i][j]+cost[j])) {
c.push(HeapNode(j,v[j]));
}
}
}
}
}
for (int i=0;i<treasureN;i++) {
cost2[tar][i]=v[treasureIn[i]];
}
cost2[tar][treasureN]=v[ss];
cost2[tar][treasureN+1]=v[tt];
}
struct HeapNode2 {
int i,j,v;
HeapNode2 (int ii,int jj,int vv) {
i=ii;j=jj;v=vv;
}
friend bool operator < (const HeapNode2 &a,const HeapNode2 &b) {
return a.v>b.v;
}
};
void calDP() {
int n=treasureN+2;
static priority_queue<HeapNode2> c;
while (!c.empty()) c.pop();
dp[n-2][0]=0;
c.push(HeapNode2(n-2,0,0));
while (!c.empty()) {
int i=c.top().i,v=c.top().v,j=c.top().j;
c.pop();
if (v==dp[i][j]) {
for (int k=0;k<n;k++) {
if (cost2[i][k]!=-1) {
int newj=(j|1<<k)&(1<<n-2)-1;
if (update(dp[k][newj],v+cost2[i][k])) {
c.push(HeapNode2(k,newj,dp[k][newj]));
}
}
}
}
}
}
int main() {
int i,j,n,s,t,x,y,z,m;
while (scanf("%d",&n)!=EOF) {
for (i=0;i<n;i++) scanf("%d",&cost[i]);
for (i=0;i<n;i++) haveTreasure[i]=false;
scanf("%d",&t);
for (i=0;i<t;i++) {
scanf("%d",&x);
haveTreasure[x-1]=true;
}
treasureN=0;
for (i=0;i<n;i++) {
if (haveTreasure[i]) {
treasureIn[treasureN++]=i;
}
}
for (i=0;i<n;i++)
for (j=0;j<n;j++)
edg[i][j]=-1;
scanf("%d",&m);
for (i=0;i<m;i++) {
scanf("%d%d%d",&x,&y,&z);
update(edg[x-1][y-1],z);
update(edg[y-1][x-1],z);
}
scanf("%d%d",&s,&t);
s--;t--;
for (i=0;i<treasureN+2;i++)
for (j=0;j<treasureN+2;j++)
cost2[i][j]=-1;
calCost(s,treasureN,n,s,t);
for (i=0;i<treasureN;i++) {
calCost(treasureIn[i],i,n,s,t);
}
/*
for (i=0;i<treasureN+2;i++) {
for (j=0;j<treasureN+2;j++)
printf("%d ",cost2[i][j]);
printf("\n");
}
*/
m=1<<treasureN;
for (i=0;i<treasureN+2;i++)
for (j=0;j<m;j++)
dp[i][j]=-1;
calDP();
printf("%d\n",dp[treasureN+1][m-1]+cost[s]);
}
return 0;
}