ZOJ 2532 Internship && POJ 3204 Ikki's Story I - Road Reconstruction【网络流的性质,最小割中的关键边】

ZOJ Problem Set - 2532
Internship

Time Limit: 5 Seconds      Memory Limit: 32768 KB

CIA headquarter collects data from across the country through its classified network. They have been using optical fibres long before it's been deployed on any civilian projects. However they are still under a lot pressure recently because the data are growing rapidly. As a result they are considering upgrading the network with new technologies that provide a few times wider bandwidth. In the experiemental stage, they would like to upgrade one segment of their original network in order to see how it performs. And as a CIA intern it's your responsibility to investigate which segment could actually help increase the total bandwidth the headquarter receives, suppose that all the cities have infinite data to send and the routing algorithm is optimized. As they have prepared the data for you in a few minutes, you are told that they need the result immediately. Well, practically immediately.

Input

Input contains multiple test cases. First line of each test case contains three integers n, m and l, they represent the number of cities, the number of relay stations and the number of segments. Cities will be referred to as integers from 1 to n, while relay stations use integers from n+1 to n+m. You can saves assume that n + m <= 100, l <= 1000 (all of them are positive). The headquarter is identified by the integer 0.

The next l lines hold a segment on each line in the form of a b c, where a is the source node and b is the target node, while c is its bandwidth. They are all integers where a and b are valid identifiers (from 0 to n+m). c is positive. For some reason the data links are all directional.

The input is terminated by a test case with n = 0. You can safely assume that your calculation can be housed within 32-bit integers.

Output

For each test print the segment id's that meets the criteria. The result is printed in a single line and sorted in ascending order, with a single space as the separator. If none of the segment meets the criteria, just print an empty line. The segment id is 1 based not 0 based.

Sample Input
2 1 3
1 3 2
3 0 1
2 0 1
2 1 3
1 3 1
2 3 1
3 0 2
0 0 0
Sample Output
2 3

&lthey here is an invisible empty line>

题目链接,请日这里:

题目大意:

给你一个图,N个流入点,M个中继点,一个汇点0,流入点有无限流输入。

再给你L条有向边(这个很关键,影响建图和搜索的性质),问这L条边中哪些边的容量增大会使总流量增大。

按“边的序号增序“输出边。

无此类边输出空行。

吐个槽:这就是传说中的“牵一发而动全身“边么~?

解法:

建超级源N+M+1,和前N个点连边,容量无限。

求一遍最大流。在残量网络上找所求边,这里不好说其定义,个人yy觉得是满流边和某些初始容量为0的边。

找这种边的两种方法:

法1

标记出我yy的这种边,然后枚举这些边,增加其容量,再做最大流。下界是L次最大流。

因为数据没有卡这种方法,所以可水过。。。

法2

从源点和汇点分别出发黑白染色。在残量网络上,源可达的点染黑色,汇可达的点(即其反边容量大于0,也即残量网络上可达汇的点)染白色。

然后按序输出,出发点为黑色,到达点为白色的边的序号。(黑1白2初始化为0)



注意点:

1.最大流用SAP,挺快的。。但是L=1000次的SAP也是不可能过这道5s的题的。。。。

2.边数为 L * 2 + N * 2

3.搜的时候其实可以限制条件其实可以加上一句,源点出发的边必须为偶数边(邻接表的编号),汇点出发的必须为奇数边。即源点开始只搜正边,汇点只搜反边。
但实际上,根本没必要,因为超级源的性质,导致所有流向流入点的正边一定不会被增广,即其反边(流入点流出的边,且编号为奇数)的残留容量一定为0。
这就保证了搜索的正确性。所以哪怕加边的时候出现(流入点到流入点)的犯二组合,也不会对搜索结果产生影响。
(此要点的关键问题在于哪些边要搜,哪些边不要搜。)
4.输出边的时候要注意,一定要出发点黑色,到达点白色才是要找的关键边。
如果出发点白色,到达点黑色,只能证明,出发点可达汇点,到达点源点可达。不能证明该边是关键边。
因为流不从这条边上流,增加容量也不会导致流量增加。

/*
Coded by BUPT-[aswmtjdsj] @ Penalty in 915
*/
/*
SAP + DFS to find key edges on the remaining network
*/
#include 
<iostream>
#include 
<cstdio>
#include 
<cstring>
#include 
<sstream>
#include 
<fstream>
#include 
<cctype>
#define maxn 120
#define maxe 4000
using namespace std;
const int inf = ~0u>>1;
struct edge
{
    
int p,next,val,anti;
    edge(){}
    edge(
int a,int b,int c,int d):p(a),next(b),val(c),anti(d){}
}v[maxn],e[maxe],arc[maxn],path[maxn];
int flow[maxn],cnt[maxn],d[maxn],pre[maxn];
int n,tot;
//n is the mark of the endpoint
//0 to n
//added to (n + 1) 's point
void init()
{
    tot 
= 0;
    
for(int i = 0;i <= n;i++)
    {
        d[i] 
= 0;
        cnt[i] 
= 0;
        v[i].next 
= -1;
    }
    cnt[
0= n + 1;
}
void add(int p,int q,int val)
{
    e[tot] 
= edge(q,v[p].next,val,tot + 1);
    v[p].next 
= tot++;
    e[tot] 
= edge(p,v[q].next,0,tot - 1);
    v[q].next 
= tot++;
}
int mflow(int s,int t)
{
    
int k,least,loc,i,now,total;
    
bool flag;
    
for(int i = 0;i <= n;i++)
        arc[i].next 
= v[i].next;
    
for(i = s,total = 0,now = inf;d[s] < n + 1;)
    {
        flow[i] 
= now;
        
for(flag = false,k = arc[i].next;~k;k = e[k].next)
        {
            
if(e[k].val > 0 && d[i] == d[e[k].p]  + 1)
            {
                now 
= min(e[k].val,now);
                pre[e[k].p] 
= i;
                path[e[k].p].next 
= k;
                arc[i].next 
= k;
                i 
= e[k].p;
                
if(i == t)
                {
                    
for(total += now;i != s;i = pre[i])
                    {
                        e[path[i].next].val 
-= now;
                        e[e[path[i].next].anti].val 
+= now;
                    }
                    now 
= inf;
                }
                flag 
= true;
                
break;
            }
        }
        
if(!flag)
        {
            
for(least = n,k = v[i].next;~k;k = e[k].next)
            {
                
if(e[k].val && d[e[k].p] < least)
                {
                    loc 
= k;
                    least 
= d[e[k].p];
                }
            }
            arc[i].next 
= loc;
            cnt[d[i]]
--;
            
if(cnt[d[i]] == 0)
                
break;
            d[i] 
= least + 1;
            cnt[d[i]]
++;
            
if(i != s)
            {
                i 
= pre[i];
                now 
= flow[i];
            }
        }
    }
    
return total;
}
struct line
{
    
int a,b,c;
    line(){}
    line(
int _a,int _b,int _c):a(_a),b(_b),c(_c){}
}l[
1005];
int color[maxn];
void dfs1(int x,int c)
{
    color[x] 
= c;
    
for(int k = v[x].next;~k;k = e[k].next)
        
if(!color[e[k].p] && e[k].val > 0)
            dfs1(e[k].p,c);
}
void dfs2(int x,int c)
{
    color[x] 
= c;
    
for(int k = v[x].next;~k;k = e[k].next)
        
if(!color[e[k].p] && e[k ^ 1].val)
            dfs2(e[k].p,c);
}
int main()
{
    
int N,M,L;
    
while(scanf("%d %d %d",&N,&M,&L) == 3 && N)
    {
        n 
= N + M + 1;
        init();
        memset(color,
0,sizeof(color));
        
for(int i = 1;i <= N;i++)
            add(n,i,inf);
        
for(int i = 1;i <= L;i++)
        {
            
int a,b,c;
            scanf(
"%d %d %d",&a,&b,&c);
            l[i] 
= line(a,b,c);
            add(a,b,c);
        }
        
int ans = mflow(n,0);
        
//cout << "ans" << ans << endl;
        
//in-order arc is all even arc
        
//re-order arc is all odd arc
        dfs1(n,1);//colored
        dfs2(0,2);
        
bool mark = false;
        
for(int i = 1;i <= L;i++)
        {
            
//if(color[l[i].a] && color[l[i].b] && color[l[i].a] != color[l[i].b]) //WA
            if(color[l[i].a] == 1 && color[l[i].b] == 2//AC
            {
                
if(!mark)
                    mark 
= true;
                
else
                    putchar(
' ');
                printf(
"%d",i);
            }
        }
        puts(
"");
    }
}

题目链接:http://poj.org/problem?id=3204
Ikki's Story I - Road Reconstruction
Time Limit: 2000MSMemory Limit: 131072K
Total Submissions: 4573Accepted: 1217

Description

Ikki is the king of a small country – Phoenix, Phoenix is so small that there is only one city that is responsible for the production of daily goods, and uses the road network to transport the goods to the capital. Ikki finds that the biggest problem in the country is that transportation speed is too slow.

Since Ikki was an ACM/ICPC contestant before, he realized that this, indeed, is a maximum flow problem. He coded a maximum flow program and found the answer. Not satisfied with the current status of the transportation speed, he wants to increase the transportation ability of the nation. The method is relatively simple, Ikki will reconstruct some roads in this transportation network, to make those roads afford higher capacity in transportation. But unfortunately, the country of Phoenix is not so rich in GDP that there is only enough money to rebuild one road. Ikki wants to find such roads that if reconstructed, the total capacity of transportation will increase.

He thought this problem for a loooong time but cannot get it. So he gave this problem to frkstyc, who put it in this POJ Monthly contest for you to solve. Can you solve it for Ikki?

Input

The input contains exactly one test case.

The first line of the test case contains two integers NM (N ≤ 500, M ≤ 5,000) which represents the number of cities and roads in the country, Phoenix, respectively.

M lines follow, each line contains three integers abc, which means that there is a road from city a to city b with a transportation capacity of c (0 ≤ ab < nc ≤ 100). All the roads are directed.

Cities are numbered from 0 to n − 1, the city which can product goods is numbered 0, and the capital is numbered n − 1.

Output

You should output one line consisting of only one integer K, denoting that there are K roads, reconstructing each of which will increase the network transportation capacity.

Sample Input

2 1 0 1 1

Sample Output

1

Source


和浙大代码不同的是,不需要输入解的方案,只需要输出关键边的数量即可。。。

注意:dfs不设置限制条件的话,这题竟然没有TLE,而是先RE(爆栈)了。。。。- -|已访问过的点一定要判定一下啊。。哪怕根据割的性质,一个点不会被两种颜色染色。

#include <iostream>
#include 
<cstdio>
#include 
<cstring>
using namespace std;
#define maxn 505
#define maxm 10100
const int inf = ~0u >> 1;
struct edge
{
    
int p,next,val,anti;
    edge(){}
    edge(
int a,int b,int c,int d):p(a),next(b),val(c),anti(d){}
}v[maxn],e[maxm],path[maxn],arc[maxn];
int flow[maxn],d[maxn],cnt[maxn],pre[maxn];
int n,m,tot;
int color[maxn];
struct line
{
    
int p,q;
    line(){}
    line(
int a,int b):p(a),q(b){}
}l[maxm];
void init()
{
    tot 
= 0;
    
for(int i = 0;i < n;i++)
    {
        v[i].next 
= -1;
        d[i] 
= 0;
        cnt[i] 
= 0;
    }
    cnt[
0= n;
}
void add(int p,int q,int c)
{
    e[tot] 
= edge(q,v[p].next,c,tot + 1);
    v[p].next 
= tot++;
    e[tot] 
= edge(p,v[q].next,0,tot - 1);
    v[q].next 
= tot++;
}
int mflow(int s,int t)
{
    
int total,i,k,least,loc,now;
    
bool flag;
    
for(int i = 0;i < n;i++)
        arc[i].next 
= v[i].next;
    
for(i = s,total = 0,now = inf;d[i] < n;)
    {
        flow[i] 
= now;
        
for(k = arc[i].next,flag = false;~k;k = e[k].next)
        {
            
if(e[k].val && d[i] == d[e[k].p] + 1)
            {
                now 
= min(now,e[k].val);
                pre[e[k].p] 
= i;
                path[e[k].p].next 
= k;
                arc[i].next 
= k;
                i 
= e[k].p;
                
if(i == t)
                {
                    
for(total += now;i != s;i = pre[i])
                    {
                        e[path[i].next].val 
-= now;
                        e[e[path[i].next].anti].val 
+= now;
                    }
                    now 
= inf;
                }
                flag 
= true;
                
break;
            }
        }
        
if(!flag)
        {
            
for(least = n,k = v[i].next;~k;k = e[k].next)
            {
                
if(e[k].val && least > d[e[k].p])
                {
                    least 
= d[e[k].p];
                    loc 
= k;
                }
            }
            arc[i].next 
= loc;
            cnt[d[i]]
--;
            
if(cnt[d[i]] == 0)
                
break;
            d[i] 
= least + 1;
            cnt[d[i]]
++;
            
if(i != s)
            {
                i 
= pre[i];
                now 
= flow[i];
            }
        }
    }
    
return total;
}
int ans;
void dfs1(int x)
{
    color[x] 
= 1;
    
for(int k = v[x].next;~k;k = e[k].next)
    {
        
if(e[k].val && !color[e[k].p])
            dfs1(e[k].p);
    }
}
void dfs2(int x)
{
    color[x] 
= 2;
    
for(int k = v[x].next;~k;k = e[k].next)
    {
        
if(e[k ^ 1].val && !color[e[k].p])
            dfs2(e[k].p);
    }
}
int main()
{
    ans 
= 0;
    scanf(
"%d %d",&n,&m);
    init();
    
for(int i = 0;i < m;i++)
    {
        
int a,b,c;
        scanf(
"%d %d %d",&a,&b,&c);
        l[i] 
= line(a,b);
        add(a,b,c);
    }
    mflow(
0,n - 1);
    memset(color,
0,sizeof(color));
    dfs1(
0);
    dfs2(n 
- 1);
    
for(int i = 0;i < m;i++)
    {
        
//cout << color[l[i].p] << color[l[i].q] << endl;
        if(color[l[i].p] == 1 && color[l[i].q] == 2)
            ans
++;
    }    
    printf(
"%d\n",ans);
    
//cout << mflow(0,n - 1) << endl;
}

posted on 2011-07-05 11:20 BUPT-[aswmtjdsj] @ Penalty 阅读(1543) 评论(1)  编辑 收藏 引用 所属分类: 图论网络流ZOJ Solution ReportPOJ Solution Report

评论

# re: ZOJ 2532 Internship && POJ 3204 Ikki's Story I - Road Reconstruction【网络流的性质,最小割中的关键边】 2012-08-31 20:51 fremn

顶,割边必须是起点与st颜色一样,终点与sink的颜色一样。不看大牛的我不着调哇多少次  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


<2012年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(1)

随笔分类(150)

随笔档案(71)

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜