题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=27024
题意:求0-(n-1)的经过最多的标记的点的最短路。
思路:首先我们可以spfa预处理出起点到标记的最短距离,标记的点到终点的最短距离,然后就是状压dp了,dp[state][u]表示在该状态下到达点u的最短路径。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define MAXN 555
#define FILL(a,b) memset(a,b,sizeof(a))
#define inf 1<<30 struct Edge{
int v,w;
Edge(int vv,int ww):v(vv),w(ww){}
}; int n,m,s,bit[<<];
int Initiate(int state)
{
int cnt=;
while(state){
cnt+=state&;
state>>=;
}
return cnt;
} int dist[][MAXN],pos[];
bool mark[MAXN];
vector<Edge>g[MAXN]; bool spfa(int vs,int dist[])
{
fill(dist,dist+n,inf);
FILL(mark,false);
queue<int>que;
que.push(vs);
dist[vs]=;
while(!que.empty()){
int u=que.front();
que.pop();
mark[u]=false;
for(int i=;i<g[u].size();i++){
int v=g[u][i].v,w=g[u][i].w;
if(dist[u]+w<dist[v]){
dist[v]=dist[u]+w;
if(!mark[v]){
mark[v]=true;
que.push(v);
}
}
}
}
return dist[n-]!=inf;
} int dp[<<][],ans1,ans2;
void Get_Dp()
{
for(int i=;i<=(<<s);i++)
for(int j=;j<=s;j++)dp[i][j]=inf;
for(int i=;i<s;i++){
int p=pos[i];
spfa(p,dist[i]);
dp[<<i][i]=dist[s][p];
}
ans1=;
ans2=dist[s][n-];
for(int state=;state<(<<s);state++){
int tmp=bit[state];
for(int i=;i<s;i++)if(state&(<<i)){
if(dist[i][n-]!=inf&&dp[state][i]!=inf){
if(tmp>ans1)ans1=tmp,ans2=dp[state][i]+dist[i][n-];
else if(tmp==ans1)ans2=min(ans2,dp[state][i]+dist[i][n-]);
for(int j=;j<s;j++)if(!(state&(<<j))){
dp[state|(<<j)][j]=min(dp[state|(<<j)][j],dp[state][i]+dist[i][pos[j]]);
}
}
}
}
printf("%d %d\n",ans1,ans2);
} int main()
{
int _case,t=;
scanf("%d",&_case);
for(int i=;i<=(<<);i++)bit[i]=Initiate(i);
while(_case--){
scanf("%d%d%d",&n,&m,&s);
for(int i=;i<=n;i++)g[i].clear();
for(int i=;i<s;i++)scanf("%d",&pos[i]);
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].push_back(Edge(v,w));
}
printf("Case %d: ",t++);
if(!spfa(,dist[s])){
puts("Impossible");
continue;
}
Get_Dp();
}
return ;
}