poj 3921 (控制费用的 最小割)

这篇博客介绍了如何解决POJ 3921问题,即防止敌人在限定时间内到达目标据点。通过将问题转化为最小割问题,博主详细阐述了如何构建图并运用费用流算法。在求解过程中,当源点到汇点的费用超过限定值时停止,最大流即为最少需要摧毁的据点数。
摘要由CSDN通过智能技术生成

题意: 

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


拆最少的点,拆点后,就转换为删最少的边 ,问题转换成最小割; 如果没有源点到汇点路径不超过k这个限制,哪就是裸的最小割了 ; 但,我们只许在求费用流的时候,当源点到汇点的费用大于k时(dist[t]>k),就退出,此时的最大流就是最小割了。

建图:对每个点i拆点为i和i+n,令源点s=1,汇点t=2*n。增加弧(1, 1+n) ,(n, 2*n) 容量都为INF,费用都为0; 而对于非源汇点, 增加弧(i, i+n)容量为1, 费用为0。若原图中存在边(u, v),增加弧(u+n, v), 容量为inf,费用为1(边权)。题目要求删点后点1到点n不存在距离大于k的路径,则对该网络求费用流,在费用大于k的时候停止(费用=边权=1).答案便是此时的最大流


#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=200;
const int M=10000;
const int inf=1<<30 ;
struct node
{
	int u,v,c,cost,next;
}edge[M];
int head[N],dist[N],pre[N],pp[N],vist[N];
int top ,n,m,k;

void add(int u ,int v,int c,int cost)            
{            
    edge[top].u=u;            
    edge[top].v=v;            
    edge[top].c=c;            
    edge[top].cost=cost;            
    edge[top].next=head[u];            
    head[u]=top++;            
    edge[top].u=v;            
    edge[top].v=u;            
    edge[top].c=0;            
    edge[top].cost=-cost;            
    edge[top].next=head[v];            
    head[v]=top++;            
}            
            
int SPFA(int s,int t)            
{            
    int u , v ;            
    memset(vist,0,sizeof(vist));            
    memset(pre,-1,sizeof(pre));            
    for(int i = 0 ; i <= t ; i++)  dist[i]=inf ;            
    vist[s]=1;dist[s]=0;pre[s]=s;            
    queue<int>q;            
    q.push(s);            
    while(!q.empty())            
    {            
         u=q.front();            
         q.pop();            
         vist[u]=0;            
         for(int i =head[u];i!=-1;i=edge[i].next)            
         {            
               v=edge[i].v;            
               if(edge[i].c && dist[v] > dist[u]+edge[i].cost)            
               {            
                    dist[v] = dist[u]+edge[i].cost ;            
                     pre[v]=u;            
                     pp[v]=i;            
                     if(!vist[v]);            
                     {            
                           vist[v]=1;            
                           q.push(v);            
                     }            
               }            
        }            
    }            
    if(dist[t]==inf) return 0;            
    return 1 ;            
}            
            
int MFMC(int s,int t)            
{            
    int mincost=0,flow=0,minflow ,ans=0;            
    while(SPFA(s,t))            
    {     
	     if(dist[t] > k)  break;      
          minflow=inf;            
          for(int i=t;i!=s;i=pre[i])            
              minflow=min(minflow,edge[pp[i]].c);            
          for(int i=t;i!=s;i=pre[i])            
          {            
                edge[pp[i]].c -= minflow;            
                edge[pp[i]^1].c += minflow;            
          }                
          flow += minflow;            
          mincost += dist[t]*minflow ;            
              
    }            
    return flow ;            
}            
      
int main()
{
	 int a,b;
	 while(~scanf("%d%d%d",&n,&m,&k))
	 {
	 	if((n+m+k)==0) break;
	 	    top = 0;
	 	    memset(head,-1,sizeof(head));
	 	    int s = 1,t=2*n ;
	 	    for(int i = 1 ; i <= m ; i++)
	 	    {
	 	    	    scanf("%d%d",&a,&b);
	 	    	    add(a+n,b,1,1); 
	 	    }
	 	    for(int i = 2 ; i < n ;i++)
			   add(i,i+n,1,0); 
			add(1,1+n,inf,0);
			add(n,n+n,inf,0);   
	 	 int ans = MFMC(s,t);
	 	 printf("%d\n",ans);
	 }
	return 0;
} 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值