【题意】大意就是一个人要从0点走到n-1点,路中有些点有商店可以买礼物,所以这个人想买多点礼物,但是又要尽快的走到n-1点,这两者之间呢,礼物的多少优先,其次是路程长度。
【分析】16个点显然是可以状压的,dp[sta][i]表示当前遍历的点的状态sta停留在shop[i]的最短路的估计值。这样状态首先是可以表示完全的,转移就显然很简单了。先预处理出每个商店到其他点的最短路径,以便后面的状态转移的需要。
【关键问题】最短路+TSP。
【最短路】这里最短路必须对n个点分别跑一遍,500个点,果断堆优化Dijstra!
【TSP】状压典型例题!dp[state][i]表示在i点,状态为state的最优答案。
【AC代码】一个巨长的ac代码,参考了别人的思路,花了两天写出并调试AC,还是太弱了~
#include <stdio.h>
#include <vector>
#include <queue>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <assert.h>
using namespace std;
const int maxn = 502;
const int maxm = 16;
const int inf = 0x3f3f3f3f;
struct edge{
int u,v,w;
edge(){}
edge(int u,int v,int w):u(u),v(v),w(w){}
};
struct node{
int u,dis;
node(){}
node(int u,int dis):u(u),dis(dis){}
bool operator<(const node &x)const
{
return dis>x.dis;
}
};
vector<edge>G[maxn];
int d[maxn][maxn];
int dp[1<<maxm][maxm];
int a[maxn];
int vis[maxn];
int n,m,t;
void Heap_Dijstra(int s)
{
node cur;
for(int i=0;i<=n;i++)d[s][i]=inf;
memset(vis,false,sizeof(vis));
d[s][s]=0;
priority_queue<node>que;
que.push(node(s,0));
while(!que.empty())
{
cur=que.top();
que.pop();
int u=cur.u;
if(vis[u])continue;
vis[u]=true;
for(int i=0;i<(int)G[u].size();i++)
{
edge temp=G[u][i];
int v=temp.v;
if(d[s][v]>cur.dis+temp.w)
{
d[s][v]=cur.dis+temp.w;
que.push(node(v,d[s][v]));
}
}
}
}
int get_num(int x)
{
int ans=0;
while(x)
{
if(x&1)ans++;
x>>=1;
}
return ans;
}
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&t);
for(int i=0;i<=n;i++)G[i].clear();
for(int i=0;i<t;i++)
{
scanf("%d",&a[i]);//where shop is !
}
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
G[u].push_back(edge(u,v,w));
}
for(int i=0;i<n;i++)
{
Heap_Dijstra(i);
}
//TSP.
for(int s=0;s<(1<<t);s++)
{
for(int i=0;i<t;i++)
{
dp[s][i]=inf;
if(!(s&(1<<i)))continue;
if(s==(1<<i)){
dp[s][i] = d[0][a[i]];
continue;
}
for(int j=0;j<t;j++)
{
if((s&(1<<j))&&(i!=j))
{
if(dp[s^(1<<i)][j]==inf)continue;
if(d[a[j]][a[i]]==inf)continue;
dp[s][i]=min(dp[s^(1<<i)][j]+d[a[j]][a[i]],dp[s][i]);
}
}
}
}
//Find answer !
int ans=inf,sum=0;
for(int s=0;s<(1<<t);s++)
{
for(int i=0;i<t;i++)
{
if(dp[s][i]==inf||d[a[i]][n-1]==inf)continue;
int temp=get_num(s);
if(sum<temp||(sum==temp&&ans>dp[s][i]+d[a[i]][n-1]))
{
sum = temp;
ans = dp[s][i]+d[a[i]][n-1];
}
}
}
printf("Case %d: ",cas++);
if(sum==0)
puts("Impossible");
else
printf("%d %d\n",sum,ans);
}
return 0;
}