HDU2485 Destroying the bus stations(最小割---点割)

Destroying the bus stations

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2319    Accepted Submission(s): 743


Problem Description
Gabiluso is one of the greatest spies in his country. Now he’s trying to complete an “impossible” mission ----- to make it slow for the army of City Colugu to reach the airport. City Colugu has n bus stations and m roads. Each road connects two bus stations directly, and all roads are one way streets. In order to keep the air clean, the government bans all military vehicles. So the army must take buses to go to the airport. There may be more than one road between two bus stations. If a bus station is destroyed, all roads connecting that station will become no use. What’s Gabiluso needs to do is destroying some bus stations to make the army can’t get to the airport in k minutes. It takes exactly one minute for a bus to pass any road. All bus stations are numbered from 1 to n. The No.1 bus station is in the barrack and the No. n station is in the airport. The army always set out from the No. 1 station.
No.1 station and No. n station can’t be destroyed because of the heavy guard. Of course there is no road from No.1 station to No. n station.


Please help Gabiluso to calculate the minimum number of bus stations he must destroy to complete his mission.
 

Input
There are several test cases. Input ends with three zeros.

For each test case:

The first line contains 3 integers, n, m and k. (0< n <=50, 0< m<=4000, 0 < k < 1000)
Then m lines follows. Each line contains 2 integers, s and f, indicating that there is a road from station No. s to station No. f.
 

Output
For each test case, output the minimum number of stations Gabiluso must destroy.
 

Sample Input
  
  
5 7 3 1 3 3 4 4 5 1 2 2 5 1 4 4 5 0 0 0
 

Sample Output
  
  
2
 

Source
 

这题求的是最少去掉几个顶点,使得图的源点和汇点不连通(不可到达或用时大于K)。想想最小割的定义:最小割C(S,T)=最大流,如果图中每条边的流量都是1,那么最小割就变成了去掉最少的边,使得图的源点和汇点不连通。把这题中每个顶点拆分成两个顶点,中间用一条流量为1的边连接(u->(u+n))...这样,模型就满足最小割模型了。还有一个限制是路径长度大于k的路径可以不用考虑。这里只需先用spfa预处理,把每个顶点到汇点的最少花费算出,用于启发式寻找满足条件的增广流路,除点自身的边花费时间为0外,其他每条边的花费是1,当路径费用大于k的时候终止最大流过程即可。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 1010;   //点的总数
const int MAXM = 400010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];
int fromeS[MAXN],fromeT[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
captype maxFlow_sap(int sNode,int eNode, int n,int maxtime){//n是包括源点和汇点的总点个数,这个一定要注意
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    memset(fromeS,-1,sizeof(fromeS));
    fromeS[sNode]=0;
    pre[sNode] = -1;
    gap[0]=n;
    captype ans=0;  //最大流
    int u=sNode ;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[i].cap-edg[i].flow){
                Min=edg[i].cap-edg[i].flow;
                inser=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow+=Min;
                edg[i^1].flow-=Min;  //可回流的边的流量
                //printf("<-%d ",edg[i].to);
            }
            ans+=Min;
            u=edg[inser^1].to;
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v , add ;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to; 
            
            //------------------寻找一条可满足条件的增广流------------------------
            add=0;
            if(u!=v-n/2) add=1; //如果从u-->v是正向流,且不是经过点自身的内部流,则需要花一个时间单位,如果是回流,说明回流的流量寻找其他可增流路
            if(edg[i].cap>0&&fromeS[u]+add+fromeT[v]>maxtime)
                continue;
            else if(edg[i].cap>0) 
                fromeS[v]=fromeS[u]+add;//printf("(%d,%d,%d) ",u,v,add);
            //---------------------------------------------------------------------
            
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if(flag){
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next){
            v = edg[i].to;  add=0;
            if(u!=v-n/2) add=1;
            if(edg[i].cap>0&&fromeS[u]+add+fromeT[v]>maxtime)
                continue;
            else if(edg[i].cap>0)
                fromeS[v]=fromeS[u]+add;
            if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
                Mind=dis[edg[i].to];
                cur[u]=i;
            }
        }

        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
    }//printf("# %d ",fromeS[2]);
    return ans;
}
void spfa(int s,int t,int n){
    queue<int>q;
    int inq[MAXN]={0};
    for(int i=1; i<=n; i++)
        fromeT[i] = INF;
    fromeT[t]=0;
    q.push(t);
    while(!q.empty()){
        int u=q.front(); q.pop();
        inq[u]=0;
        for(int i=head[u]; i!=-1; i=edg[i].next){
            int v=edg[i].to , add = 0;
            if(u!=v+n/2) add=1;
            if(edg[i].cap==0&&fromeT[edg[i].to]>fromeT[u]+add){//都取反向边
                fromeT[edg[i].to]=fromeT[u]+add;
                if(!inq[v])
                    inq[v]=1,q.push(v);
            }
        }
    }
}
int main(){
    int n,m,k;
    int u,v;
    while(scanf("%d%d%d",&n,&m,&k)>0){
        if(n==0&&m==0&&k==0)
            break;
        init();
        for(int i=1; i<=n; i++)
            addEdg(i,i+n,1);
        while(m--){
            scanf("%d%d",&u,&v);
            addEdg(u+n,v,1);
        }
        spfa(1+n,n,n*2);//printf("%d  ",fromeT[1+n]);
        int ans=maxFlow_sap(1+n , n , n*2, k);
        printf("%d\n",ans);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值