码迷,mamicode.com
首页 > 其他好文 > 详细

最小截断[AHOI2009]

时间:2017-07-31 21:14:23      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:扩大   break   bool   ace   str   路径   i++   格式   add   

【题目描述】

宇宙旅行总是出现一些意想不到的问题,这次小可可所驾驶的宇宙飞船所停的空间站发生了故障,这个宇宙空间站非常大,它由N个子站组成,子站之间有M条单向通道,假设其中第i(1<=i<=M)条单向通道连接了xi,yi两个中转站,那么xi子站可以通过这个通道到达yi子站,如果截断这条通道,需要代价ci。现在为了将故障的代价控制到最小,小可可必须想出一个截断方案,使a站不能到达b子站,并且截断的代价之和最小。我们称之为最小截断,小可可很快解决了这个故障,但是爱思考的小可可并不局限于此,为了今后更方便的解决同类故障,他考虑对每条单向通道:

1,是否存在一个最小代价路径截断方案,其中该通道被切断?

2,是否对任何一个最小代价路径切断方案,都有该通道被切断?

聪明的你能帮小可可解决他的疑问吗?

 

【输入格式】

第一行有4个整数,依次为N,M,a和b;

第二行到第(m+1)行每行3个正整数x,y,c表示x子站到y子站之间有单向通道相连,单向通道的起点是x终点是y,切断它的代价是c(1<=c<=10000);

两个子站之间可能有多条通道直接连接。

 

【输出格式】

对每一个单向通道,按输入的顺序,依次输出一行包含两个非0即1的整数,分别表示对问题一和问题二的回答(其中1表示是,0表示否)。每行两个整数之间用一个空格分隔开。

 

【样例输入】

   6 7 1 6 
   1 2 3
   1 3 2
   2 4 4 
   2 5 1
   3 5 5 
   4 6 2
   5 6 3

【样例输出】

1 0
1 0
0 0
1 0
0 0
1 0
1 0

【提示】

  100%的数据中,N<=4000,M<=60000

  70%的数据中,N<=1000,M<=40000

  40%的数据中,N<=200,M<=2000

 

【题解】

     血帆海盗的进阶版,先贴结论XDDD:

       最小割的必须边
       一定在最小割中的边、扩大容量后能增大最大流的边, ① 满流;② 残余网络中S能到入点、出点能到T。 从S开始DFS、T开始反向DFS,标记到达的点,然后枚举满流边即可。
       最小割的可行边
       被某一种最小割的方案包含的边, ① 满流;② 删掉之后在残余网络中找不到u到v的路径。 在残余网络中tarjan求SCC,(u,v)两点在同一SCC中说明残余网络中存在u到v路径。

      在这道题里求必须边也可以用tarjan搞定,必须边的起点与S在同一个强联通分量里,终点与T在同一个强联通分量里。知道了结论之后就可以放心地跑了。

 

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;
const int sj=4010;
int n,m,s,t,e,a1,a2,a3;
int dep[sj],dfn[sj],c[sj],low[sj],h[sj];
stack<int> z;
queue<int> q;
bool r[sj];
struct B
{
    int ne,v,w,u;
}b[120010];
void add(int x,int y,int z)
{
     b[e].v=y;
     b[e].u=x;
     b[e].w=z;
     b[e].ne=h[x];
     h[x]=e++;
     b[e].v=x;
     b[e].w=0;
     b[e].u=y;
     b[e].ne=h[y];
     h[y]=e++;
}
bool bfs(int x)
{
     while(!q.empty()) q.pop();
     memset(dep,0,sizeof(dep));
     dep[x]=1;
     q.push(x);
     while(!q.empty())
     {
        x=q.front();
        q.pop();
        for(int i=h[x];i!=-1;i=b[i].ne)
          if(b[i].w&&!dep[b[i].v])
          {
             dep[b[i].v]=dep[x]+1;
             if(b[i].v==t) return 1;
             q.push(b[i].v);
          }
     }
     return 0;
}
int bj(int x,int y)
{
    return x<y?x:y;
}
int dfs(int x,int f)
{
    if(x==t)  return f; 
    int ans=0,d;
    for(int i=h[x];i!=-1;i=b[i].ne)
      if(dep[b[i].v]>dep[x]&&b[i].w)
      {
         d=dfs(b[i].v,bj(b[i].w,f));
         f-=d;
         ans+=d;
         b[i].w-=d;
         b[i^1].w+=d;
         if(!f) break;
      }
    if(!ans) dep[x]=-1;
    return ans;
}
void tarjan(int x)
{
     low[x]=dfn[x]=++a1;
     z.push(x);
     r[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
     {
        if(!b[i].w) continue;
        if(!dfn[b[i].v])
        {
           tarjan(b[i].v);
           low[x]=bj(low[x],low[b[i].v]);
        }
        else if(r[b[i].v])
           low[x]=bj(low[x],dfn[b[i].v]);
     }
     if(low[x]==dfn[x])
     {
        a2++;
        do
        {
           a3=z.top();
           z.pop();
           c[a3]=a2;
           r[a3]=0;
        }while(a3!=x);
     }
}
void init()
{
     scanf("%d%d%d%d",&n,&m,&s,&t);
     memset(h,-1,sizeof(h));
     for(int i=1;i<=m;i++)
     {
       scanf("%d%d%d",&a1,&a2,&a3);
       add(a1,a2,a3);
     }
     while(bfs(s)) dfs(s,0x7fffffff);
     a1=a2=0;
     for(int i=1;i<=n;i++)
       if(!dfn[i]) tarjan(i);
     s=c[s];
     t=c[t];
}
void cl()
{
     for(int i=0;i<e;i+=2)
     {
        if(b[i].w) printf("0 0\n");
        if(!b[i].w)
        {
           if(c[b[i].u]!=c[b[i].v])
           {
              if(c[b[i].u]==s&&c[b[i].v]==t)
                printf("1 1\n");
              else printf("1 0\n");
           } 
           else printf("0 0\n");
        }
     }
}
int main()
{
    init();
    cl();
    return 0;
}
mincut

 

最小截断[AHOI2009]

标签:扩大   break   bool   ace   str   路径   i++   格式   add   

原文地址:http://www.cnblogs.com/moyiii-/p/7265664.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!