51nod 1171 大灾变

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1757

二分答案mid

避难所拆为mid个点

每个避难所的第一个点向第二个点,第二个点向第三个点……连inf边

每个点向汇点连流量为1的边

枚举能在mid时间内到达避难所i的点j,假设时间为t,由点j向点i的第t个点连流量为1的边

源点向每个非避难所节点连流量为1的边

最大流判断能否==n-m

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2001
using namespace std;
int n,m,p,src,decc,cnt;
int front[N*200],nxt[N*500],to[N*500],cap[N*500],tot;
int dis[41][N];
int cur[N*200],lev[N*200];
bool vis[N*200],safe[N];
const int inf=2e9;
void add(int u,int v,int w)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; cap[tot]=w;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; cap[tot]=0;
}
void build(int mid)
{
    memset(front,0,sizeof(front));
    tot=1;    decc=n+m*mid+1;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<mid;j++) 
         add(n+mid*(i-1)+j,n+mid*(i-1)+j+1,inf);
        for(int j=1;j<=n;j++)
            if(!safe[j] && dis[i][j]<=mid) 
                add(j,n+mid*(i-1)+dis[i][j],1);
    }
    for(int i=n+1;i<decc;i++) add(i,decc,inf);
    for(int i=1;i<=n;i++) if(!safe[i]) add(src,i,1);
}
bool bfs()
{
    for(int i=0;i<=decc;i++) cur[i]=front[i],lev[i]=-1;
    queue<int>q;
    vis[src]=true;
    lev[src]=0;
    q.push(src);
    int now;
    while(!q.empty())
    {
        now=q.front();
        q.pop(); vis[now]=false;
        for(int i=front[now];i;i=nxt[i])
            if(lev[to[i]]==-1 && cap[i]>0)
            {
                lev[to[i]]=lev[now]+1;
                if(to[i]==decc) return true;
                if(!vis[to[i]])
                {
                    vis[to[i]]=true;
                    q.push(to[i]);
                }
            }
    }
    return false;
}
int dinic(int now,int flow)
{
    if(now==decc) return flow;
    int rest=0,delta;
    for(int & i=cur[now];i;i=nxt[i])
        if(lev[to[i]]>lev[now] && cap[i]>0)
        {
            delta=dinic(to[i],min(cap[i],flow-rest));
            if(delta)
            {
                cap[i]-=delta,cap[i^1]+=delta;
                rest+=delta; 
                if(rest==flow) break;
            }
        }
    if(rest!=flow) lev[now]=-1;
    return rest;
}
bool check(int mid)
{
    build(mid);
    int now=0;
    while(bfs()) now+=dinic(src,inf);
    if(now==n-m) return true;
    return false;
}
void dfs(int x,int f,int s)
{
    for(int i=front[x];i;i=nxt[i])
        if(to[i]!=f)
        {
            dis[s][to[i]]=dis[s][x]+1;
            dfs(to[i],x,s);
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v,0);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&u);
        safe[u]=true;
        dfs(u,0,i);
    }
    int l=1,r=n+1,mid,ans;
    while(l<=r)
    {
        mid=l+r>>1;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}
基准时间限制:3 秒 空间限制:262144 KB 分值: 160 难度:6级算法题
 收藏
 关注
死亡之翼降临了!艾泽拉斯大陆的子民们必须逃出他的魔爪!
艾泽拉斯的结构是一棵树,这棵树上的一些节点是地精建造的通往地下避难所的洞口。
除了这些洞口之外,树上的每个节点上都有一个种族,每个种族通过树上的一条边都需要一个单位时间。
因为地精比较矮小,所以洞口很窄,每个单位时间只能让一个种族通过,但是一个单位时间内的一个节点上可以存在多个种族。
地精们需要你求出最少需要多少单位时间才能让所有种族躲进地下避难所。
【注意题目有修改,洞口不一定是叶子节点】
Input
第1行两个整数n(n<=2000)和m(m<=40)表示节点个数和洞口个数
接下来n-1行每行两个整数表示树上的每一条边
第n+1行m个整数表示所有洞口的编号,保证洞口是叶子节点
Output
一个整数t表示让所有种族躲进地下避难所的最少时间
Input示例
6 2
1 2
1 3
1 4
1 5
5 6
3 6
Output示例
3
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7512407.html