POJ 3921 - Destroying the bus stations 构图最小费用最大流..

                  题意: 

                            有N个据点..敌人要从1号据点往N号据点去..有些据点间存在直接路径..敌人走一条路径都为一个单位时间...而咱可以摧毁一个据点..这么做就让与这个据点相连的路径全部作废..问为了防止敌人在K单位时间内到达N点..至少要摧毁几个据点..注意的是1、N号据点是不能摧毁的...并且1与N没有直接的路径..

                  题解:

                            一个点被摧毁了..与其相连的路径作废..这就是求图的最小点割...马上想到要拆点..把一个点拆成起点和终点..并且做容量为1的边...然后就是构图..如果没有加入时间K..那就是裸最小点割..跑最大流就行.但这道题就需要用最小费用最大流来做了...其实也差不多..每个点的起点和终点边的费用为0..其余边的费用都为1..跑最小费用最大流..当费用大于了K就停止..此时的最大流等于此时的最小割..也就是答案..


Program:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#define MAXN 505
#define MAXM 50005
#define oo 1000000007
#define ll long long
using namespace std;  
struct MCMF  
{  
       struct node  
       {  
              int x,y,c,v,next;   
       }line[MAXM];  
       int Lnum,_next[MAXN],pre[MAXN],dis[MAXN],flow,cost;  
       bool inqueue[MAXN];  
       void initial(int n)  
       {  
              Lnum=-1;  
              for (int i=0;i<=n;i++) _next[i]=-1;  
       }  
       void addline(int x,int y,int c,int v)  
       {  
              line[++Lnum].next=_next[x],_next[x]=Lnum;  
              line[Lnum].x=x,line[Lnum].y=y,line[Lnum].c=c,line[Lnum].v=v;  
              line[++Lnum].next=_next[y],_next[y]=Lnum;  
              line[Lnum].x=y,line[Lnum].y=x,line[Lnum].c=0,line[Lnum].v=-v;  
       }  
       bool SPFA(int s,int e,int K)  
       {  
              int x,k,y;  
              queue<int> Q;  
              while (!Q.empty()) Q.pop();  
              memset(dis,0x7f,sizeof(dis));  
              memset(inqueue,false,sizeof(inqueue));  
              Q.push(s);  
              dis[s]=0,pre[s]=-1;  
              while (!Q.empty())  
              {  
                      x=Q.front(),Q.pop(),inqueue[x]=false;  
                      for (k=_next[x];k!=-1;k=line[k].next)    
                         if (line[k].c)  
                         {  
                               y=line[k].y;  
                               if (dis[y]>dis[x]+line[k].v)  
                               {  
                                        dis[y]=dis[x]+line[k].v;  
                                        pre[y]=k;  
                                        if (!inqueue[y])  
                                        {  
                                                inqueue[y]=true;  
                                                Q.push(y);  
                                        }  
                               }  
                         }  
              }  
              if (dis[e]>K) return false;  
              flow=oo,cost=0;  
              for (k=pre[e];k!=-1;k=pre[line[k].x])   
                  flow=min(flow,line[k].c),cost+=line[k].v;      
              cost*=flow;  
              for (k=pre[e];k!=-1;k=pre[line[k].x])  
                  line[k].c-=flow,line[k^1].c+=flow;    
              return true;  
       }  
       int MinCostMaxFlow(int s,int e,int k)  
       {  
              int Aflow=0,Acost=0;  
              while (SPFA(s,e,k))  
              {  
                     Aflow+=flow;  
                     Acost+=cost;   
              }  
              return Aflow;  
       }  
}T;   
int main() 
{        
      int n,m,k,s,e,x,y; 
      while (~scanf("%d%d%d",&n,&m,&k) && n)
      {
                s=1,e=(n-1)<<1,T.initial(n<<1|1);
                while (m--)
                {
                        scanf("%d%d",&x,&y),x--,y--;
                        T.addline(x<<1|1,y<<1,oo,1); 
                }
                for (x=0;x<n;x++) T.addline(x<<1,x<<1|1,1,0);
                printf("%d\n",T.MinCostMaxFlow(s,e,k));
      }      
      return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值