HDU 4411 arrest

题目大意:给出一张N+1个点和M条边的带权有向图,代表有N+1个城市和M条路,在0有警察局,1-N这些城市有犯罪分子,现在给你K个条子,从0从发,要求按从小到大的顺序依次收拾掉这些犯罪分子,每个条子到达一个城市的时候,他可以收拾这里的人,也可以不收拾,但绝不能让第i+1个城市的人早与第i个城市的人被干掉,求让这些人从0出发完成任务后回到0的走过的最短路之和。

解法:这里给的K个条子可以不用完。。所以有两条边(S,0,K,0),(0,T,K,0)。然后为了保证每个点都被遍历到,需要拆点建边,并且保证这条边一定会走到(i,i',-oo,0)。对于城市1-N,从0出发最后要回到0,对应两条边(0,i,1,dis[0][i]),(i',T,1,dis[0][1])。对于城市i<j,对应一条边(i',j,1,dis[i][j])。跑最小费用流,ans+N*oo就是结果。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <queue>
  5 #define maxn 210
  6 #define maxm 101000
  7 #define INF 1<<29
  8 #define oo 1000000
  9 using namespace std;
 10 struct MCMF{
 11     int src,sink,e,n;
 12     int first[maxn];
 13     int cap[maxm],cost[maxm],v[maxm],next[maxm];
 14     bool flag;
 15     void init(){
 16         e = 0;
 17         memset(first,-1,sizeof(first));
 18     }
 19 
 20     void add_edge(int a,int b,int cc,int ww){
 21         //printf("add:%d to %d,cap = %d,cost = %d\n",a,b,cc,ww);
 22         cap[e] = cc;cost[e] = ww;v[e] = b;
 23         next[e] = first[a];first[a] = e++;
 24         cap[e] = 0;cost[e] = -ww;v[e] = a;
 25         next[e] = first[b];first[b] = e++;
 26     }
 27 
 28     int d[maxn],pre[maxn],pos[maxn];
 29     bool vis[maxn];
 30 
 31     bool spfa(int s,int t){
 32         memset(pre,-1,sizeof(pre));
 33         memset(vis,0,sizeof(vis));
 34         queue<int> Q;
 35         for(int i = 0;i <= n;i++)   d[i] = INF;
 36         Q.push(s);pre[s] = s;d[s] = 0;vis[s] = 1;
 37         while(!Q.empty()){
 38             int u = Q.front();Q.pop();
 39             vis[u] = 0;
 40             for(int i = first[u];i != -1;i = next[i]){
 41                 if(cap[i] > 0 && d[u] + cost[i] < d[v[i]]){
 42                     d[v[i]] = d[u] + cost[i];
 43                     pre[v[i]] = u;pos[v[i]] = i;
 44                     if(!vis[v[i]])  vis[v[i]] = 1,Q.push(v[i]);
 45                 }
 46             }
 47         }
 48         return pre[t] != -1;
 49     }
 50 
 51     int Mincost;
 52     int Maxflow;
 53 
 54     int MinCostFlow(int s,int t,int nn){
 55         Mincost = 0,Maxflow = 0,n = nn;
 56         while(spfa(s,t)){
 57             int min_f = INF;
 58             for(int i = t;i != s;i = pre[i])
 59                 if(cap[pos[i]] < min_f) min_f = cap[pos[i]];
 60             Mincost += d[t] * min_f;
 61             Maxflow += min_f;
 62             for(int i = t;i != s;i = pre[i]){
 63                 cap[pos[i]] -= min_f;
 64                 cap[pos[i]^1] += min_f;
 65             }
 66         }
 67         return Mincost;
 68     }
 69 };
 70 MCMF g;
 71 int N,M,K;
 72 int f[maxn][maxn];
 73 inline int min(int a,int b){
 74     return a < b ? a : b;
 75 }
 76 void floyd(){
 77     for(int k = 0;k <= N;k++)
 78         for(int i = 0;i <= N;i++)
 79             for(int j = 0;j <= N;j++)
 80                 if(f[i][j] > f[i][k] + f[k][j])
 81                     f[i][j] = f[i][k] + f[k][j];
 82 }
 83 
 84 int main(){
 85     while(scanf("%d%d%d",&N,&M,&K),N+M+K){
 86         for(int i = 0;i <= N;i++)
 87             for(int j = 0;j <= N;j++)
 88                 f[i][j] = INF;
 89         for(int i = 0;i < M;i++){
 90             int a,b,c;
 91             scanf("%d%d%d",&a,&b,&c);
 92             f[a][b] = f[b][a] = min(c,f[a][b]);
 93         }
 94         for(int i = 0;i <= N;i++)
 95             f[i][i] = 0;
 96         floyd();
 97         g.init();
 98         int S = 2*N+1,T = S+1;
 99         g.add_edge(S,0,K,0);g.add_edge(0,T,K,0);
100         for(int i = 1;i <= N;i++)
101             g.add_edge(0,i,1,f[0][i]),g.add_edge(i+N,T,1,f[0][i]);
102         for(int i = 1;i <= N;i++)
103             g.add_edge(i,i+N,1,-oo);
104         for(int i = 1;i <= N;i++)
105             for(int j = i+1;j <= N;j++)
106                 g.add_edge(i+N,j,1,f[i][j]);
107         int ans = g.MinCostFlow(S,T,T) + N*oo;
108         printf("%d\n",ans);
109     }
110     return 0;
111 }
View Code

 

转载于:https://www.cnblogs.com/zhexipinnong/p/3371222.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值