loj 1316(spfa预处理+状压dp)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=27024

题意:求0-(n-1)的经过最多的标记的点的最短路。

思路:首先我们可以spfa预处理出起点到标记的最短距离,标记的点到终点的最短距离,然后就是状压dp了,dp[state][u]表示在该状态下到达点u的最短路径。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #include<vector>
  7 using namespace std;
  8 #define MAXN 555
  9 #define FILL(a,b) memset(a,b,sizeof(a))
 10 #define inf 1<<30
 11 
 12 struct Edge{
 13     int v,w;
 14     Edge(int vv,int ww):v(vv),w(ww){}
 15 };
 16 
 17 int n,m,s,bit[1<<17];
 18 int Initiate(int state)
 19 {
 20     int cnt=0;
 21     while(state){
 22         cnt+=state&1;
 23         state>>=1;
 24     }
 25     return cnt;
 26 }
 27 
 28 int dist[17][MAXN],pos[17];
 29 bool mark[MAXN];
 30 vector<Edge>g[MAXN];
 31 
 32 bool spfa(int vs,int dist[])
 33 {
 34     fill(dist,dist+n,inf);
 35     FILL(mark,false);
 36     queue<int>que;
 37     que.push(vs);
 38     dist[vs]=0;
 39     while(!que.empty()){
 40         int u=que.front();
 41         que.pop();
 42         mark[u]=false;
 43         for(int i=0;i<g[u].size();i++){
 44             int v=g[u][i].v,w=g[u][i].w;
 45             if(dist[u]+w<dist[v]){
 46                 dist[v]=dist[u]+w;
 47                 if(!mark[v]){
 48                     mark[v]=true;
 49                     que.push(v);
 50                 }
 51             }
 52         }
 53     }
 54     return dist[n-1]!=inf;
 55 }
 56 
 57 int dp[1<<17][17],ans1,ans2;
 58 void Get_Dp()
 59 {
 60     for(int i=0;i<=(1<<s);i++)
 61         for(int j=0;j<=s;j++)dp[i][j]=inf;
 62     for(int i=0;i<s;i++){
 63         int p=pos[i];
 64         spfa(p,dist[i]);
 65         dp[1<<i][i]=dist[s][p];
 66     }
 67     ans1=0;
 68     ans2=dist[s][n-1];
 69     for(int state=0;state<(1<<s);state++){
 70         int tmp=bit[state];
 71         for(int i=0;i<s;i++)if(state&(1<<i)){
 72             if(dist[i][n-1]!=inf&&dp[state][i]!=inf){
 73                 if(tmp>ans1)ans1=tmp,ans2=dp[state][i]+dist[i][n-1];
 74                 else if(tmp==ans1)ans2=min(ans2,dp[state][i]+dist[i][n-1]);
 75                 for(int j=0;j<s;j++)if(!(state&(1<<j))){
 76                     dp[state|(1<<j)][j]=min(dp[state|(1<<j)][j],dp[state][i]+dist[i][pos[j]]);
 77                 }
 78             }
 79         }
 80     }
 81     printf("%d %d\n",ans1,ans2);
 82 }
 83 
 84 
 85 
 86 int main()
 87 {
 88     int _case,t=1;
 89     scanf("%d",&_case);
 90     for(int i=0;i<=(1<<15);i++)bit[i]=Initiate(i);
 91     while(_case--){
 92         scanf("%d%d%d",&n,&m,&s);
 93         for(int i=0;i<=n;i++)g[i].clear();
 94         for(int i=0;i<s;i++)scanf("%d",&pos[i]);
 95         while(m--){
 96             int u,v,w;
 97             scanf("%d%d%d",&u,&v,&w);
 98             g[u].push_back(Edge(v,w));
 99         }
100         printf("Case %d: ",t++);
101         if(!spfa(0,dist[s])){
102             puts("Impossible");
103             continue;
104         }
105         Get_Dp();
106     }
107     return 0;
108 }
View Code

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值