[USACO09MAR]地震损失2Earthquake Damage 2

地震破坏

时间限制: 1 Sec  内存限制: 128 MB

题目描述

威斯康星发生了一场地震!约翰的牧场遭到了打击,有一些牛棚变成了废墟,如果一间牛棚遭到
了破坏,那么所有和它相连的道路都不能使用了。
约翰有 N 个牛棚,编号为 1 到 N,有 M 条双向道路连接这些牛棚,第 i 条道路连接的牛棚是
A i 和 B i ,A i 可能等于 B i ,也可能有多条道路连接同一对牛棚。
约翰让奶牛们集中到 1 号牛棚避难。有 P 头奶牛通过手机向约翰求救,她们的遭遇类似:好消
息是她们所在的牛棚没有损坏,坏消息是由于其他的牛棚遭到破坏,所以她们找不到任何可以走到 1
号牛棚的路径。假设发送报告的第 i 头奶牛目前在牛棚 R i 里,那么以最乐观的情况来看,至少有多
少牛棚被破坏了呢?

 

输入

• 第一行:三个整数 N,M 和 P, 1 ≤ P ≤ N ≤ 3000, 1 ≤ M ≤ 20000
• 第二行到第 M + 1 行:第 i + 1 行有两个整数 A i 和 B i ,1 ≤ A i ,B i ≤ N
• 第 M + 2 行到第 M + P + 1 行:第 i + M + 1 行有一个整数 R i , 1 ≤ R i ≤ N,保证每个 R i 都
是不同的

 

输出

• 单个整数:表示至少有几个牛棚被破坏了

 

样例输入

5 5 2
1 2
2 3
3 5
2 4
4 5
4
5

样例输出

1

提示

最乐观的情况是只有 2 号牛棚坏了

题解:

一道比较经典的最小割,首先因为每次删除的是一个点,所以要将每个点拆开,将1节点和打电话过来的几个节点残量设为最大值,其余的为1。(为了让割边落在可以去掉的点上)这样就可以将去点转化为割边。

然后根据图的连接方式,若节点x被分为x1和x2,节点y被分为y1和y2。若x和y连接,则将x2连y1,y2连x1,残量都为最大值。

然后跑一遍网络流就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#define inf (2e8)
using namespace std;
int n,m,l;
struct node
{
    int next,to,cap;
}edge[200001];
int head[100001],size=1,a[190001];
void putin(int from,int to,int cap)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].cap=cap;
    head[from]=size;
}
void in(int from,int to,int cap)
{
    putin(from,to,cap);
    putin(to,from,0);
}
int dist[100001],numbs[100001];
void bfs(int src,int des)
{
    int i;
    queue<int>mem;
    mem.push(des);
    dist[des]=0;numbs[0]++;
    while(!mem.empty())
    {
        int x=mem.front();mem.pop();
        for(i=head[x];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;
            if(edge[i].cap==0&&dist[y]==0&&y!=des)
            {
                dist[y]=dist[x]+1;
                numbs[dist[y]]++;
                mem.push(y);
            }
        }
    }
    return;
}
int dfs(int src,int flow,int des)
{
    if(src==des)return flow;
    int i,low=0,mindist=n*2+2;
    for(i=head[src];i!=-1;i=edge[i].next)
    {
        int y=edge[i].to;
        if(edge[i].cap)
        {
            if(dist[y]==dist[src]-1)
            {
                int t=dfs(y,min(flow-low,edge[i].cap),des);
                edge[i].cap-=t;
                edge[i^1].cap+=t;
                low+=t;
                if(dist[src]>=n*2+2)return low;
                if(low==flow)break;
            }
            mindist=min(mindist,dist[y]+1);
        }
    }
    if(!low)
    {
        if(!(--numbs[dist[src]]))dist[0]=n*2+2;
        ++numbs[dist[src]=mindist];
    }
    return low;
}
int ISAP(int src,int des)
{
    int ans=0;
    bfs(src,des);
    while(dist[0]<n*2+2)ans+=dfs(src,2e8,des);
    return ans;
}
int main()
{
    int i,j;
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&m,&l);
    for(i=1;i<=m;i++)
    {
        int from,to;
        scanf("%d%d",&from,&to);
        in(n+from,to,inf);
        in(n+to,from,inf);
    }
    for(i=1;i<=l;i++){scanf("%d",&j);a[j]=1;}
    in(1,n+1,inf);
    for(i=2;i<=n;i++)
    {
        if(a[i]==1){in(i,n+i,inf);in(n+i,2*n+1,inf);}
        else in(i,n+i,1);
    }
    in(0,1,inf);
    int maxflow=ISAP(0,n*2+1);
    cout<<maxflow;
    return 0;
}

 

posted @ 2017-05-28 13:30  kakakakakaka  阅读(324)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效