网络流建图三题

1.BZOJ2039人员雇佣

Description

作为一个富有经营头脑的富翁,小L决定从本国最优秀的经理中雇佣一些来经营自己的公司。这些经理相互之间合作有一个贡献指数,(我们用Ei,j表示i经理对j经理的了解程度),即当经理i和经理j同时被雇佣时,经理i会对经理j做出贡献,使得所赚得的利润增加Ei,j。当然,雇佣每一个经理都需要花费一定的金钱Ai,对于一些经理可能他做出的贡献不值得他的花费,那么作为一个聪明的人,小L当然不会雇佣他。 然而,那些没有被雇佣的人会被竞争对手所雇佣,这个时候那些人会对你雇佣的经理的工作造成影响,使得所赚得的利润减少Ei,j(注意:这里的Ei,j与上面的Ei,j 是同一个)。 作为一个效率优先的人,小L想雇佣一些人使得净利润最大。你可以帮助小L解决这个问题吗?

Input

第一行有一个整数N<=1000表示经理的个数 第二行有N个整数Ai表示雇佣每个经理需要花费的金钱 接下来的N行中一行包含N个数,表示Ei,j,即经理i对经理j的了解程度。(输入满足Ei,j=Ej,i)

Output

第一行包含一个整数,即所求出的最大值。

Sample Input

3

3 5 100

0 6 1

6 0 2

1 2 0

Sample Output

1

【数据规模和约定】

20%的数据中N<=10

50%的数据中N<=100

100%的数据中 N<=1000, Ei,j<=maxlongint, Ai<=maxlongint

2.BZOJ2127happiness

Description

高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。

Input

第一行两个正整数n,m。接下来是六个矩阵第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择文科获得的喜悦值。第二个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择理科获得的喜悦值。第三个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择文科获得的额外喜悦值。第四个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择理科获得的额外喜悦值。第五个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择文科获得的额外喜悦值。第六个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择理科获得的额外喜悦值。

Output

输出一个整数,表示喜悦值总和的最大值

Sample Input

1 2

1 1

100 110

1

1000

Sample Output

1210

【样例说明】

两人都选理,则获得100+110+1000的喜悦值。

【数据规模】

对于100%以内的数据,n,m<=100 所有喜悦值均为小于等于5000的非负整数

3.POJ3041(这是到二分图的题)

题目描述

Bessie想驾驶她的飞船穿过危险的小行星群,小行星群是一个N×N的网格(1 <= N <= 500),在网格内有K个小行星(1 <= K <= 10,000)。
幸运地是Bessie有一个很强大的武器,一次可以消除所有在一行或一列中的小行星,这种武器很贵,所以她希望尽量地少用。给出所有的小行星的位置,算出Bessie最少需要多少次射击就能消除所有的小行星。

输入

第1行:两个整数N和K,用一个空格隔开。
第2行至K+1行:每一行有两个空格隔开的整数R和C(1 <= R, C <= N),分别表示小行星所在的行和列。

输出

1行:一个整数表示Bessie需要的最少射击次数,可以消除所有的小行星。

样例输入

3 4
1 1
1 3
2 2
3 2

样例输出

2

样例解释

【输入解释】:
下面的图表示上面的数据,”X”表示一个小行星,”.”表示为空:
X.X
.X.
.X.
【输出解释】:
Bessie在第1行射击消除在(1,1)和(1,3)上的小行星,在第2列射击消除在(2,2)和(3,2)上的小行星。


这三道题的关键都是建图,我们一道一道讲:

1.我们先假设雇佣了所有员工,那么我们用sum来记录所有人的贡献值。那我们再想,我们如果雇佣了一个员工,需要什么代价?需要支付他工资,并且要减去所有没雇佣的员工对他的干扰。如果我们不顾用某个员工,那我们还得减去他的贡献值。所以我们考虑这么建图:超级源点S与每个点i连上等于c[i]的边(c[i]为雇佣i的代价),每个点和超级汇点连上等于他所有贡献的边,而每个点两两之间连上他们的Ei,j*2(因为不仅没了贡献,还有干扰)
如图即为题目样例的建图,我们显然发现,这张图最小割就是我们需要减去的最小代价,所以我们跑一边最大流,求出最小割即可

这里写图片描述

代码附上:
#include<bits/stdc++.h>
using namespace std;
int i,j,k,n,m,tot,ans,S,T,h,t,cnt;
int head[1005],dis[1005],nxt[5000005],q[6000005]; 
struct node{
    int to,val;
}L[5000005];
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int f[1005][1005],a[1005];
void add(int x,int y,int c){
    L[cnt]=(node){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(node){x,c};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
int BFS(){
    memset(dis,0,sizeof(dis));
    dis[S]=1;t=h=0;q[++t]=S;
    while(h<t){
        int front=q[++h];
        for(int i=head[front];i!=-1;i=nxt[i]){
            int to=L[i].to;
            if(!dis[to]&&L[i].val){
                dis[to]=dis[front]+1;
                q[++t]=to;
            }
        }
    }
    return dis[T];
}
int DFS(int now,int x){
    if(now==T) return x;
    int res=0;
    for(int i=head[now];i!=-1&&x;i=nxt[i]){
        int to=L[i].to;
        if(dis[to]==dis[now]+1&&L[i].val){
            int fd=DFS(to,min(x,L[i].val));
            x-=fd;res+=fd;
            L[i].val-=fd;L[i^1].val+=fd;
        }
    }
    if(!res) dis[now]=0;
    return res;
}
int main()
{
    n=read();S=0;T=n+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) 
     for(int j=1;j<=n;j++) f[i][j]=read(),tot+=f[i][j];
    for(int i=1;i<=n;i++) add(S,i,a[i]);
    for(int i=1;i<=n;i++)
     for(int j=i+1;j<=n;j++) add(i,j,f[i][j]*2);
    for(int i=1;i<=n;i++){
        int res=0;
        for(int j=1;j<=n;j++) res+=f[i][j];
        add(i,T,res);
    }
    while(BFS()) 
      ans+=DFS(S,2e9);
    printf("%d",tot-ans);
    return 0;
}

2.这道题我们依旧考虑最小割做法。
我们先用sum表示所有人文科理科都选能获得的最打快乐指数,但显然这是不可能的,我们需要让一些人放弃选文科或理科,那我们这么建图:S到点i连(i选文科的快乐指数+i和周边人都选文科的附加欢乐指数)/2的边,i到T连(i选理科的快乐指数+i和周边人都选理科的附加欢乐指数)/2,点i到他边上的点连(都选文科的附加欢乐指数+都选理科的附加欢乐指数)/2。
为什么这么连?大家可以自己拿草稿纸,在纸上用样例数据画一下这个图,然后我们会发现:这张图的最小割等于我们需要舍弃的最小值,我们用sum减去最小割,得到的就是符合文理科分选的最大欢乐值了。
#include<bits/stdc++.h>
using namespace std;
int n,m,l[105][105],w[105][105],cnt,sum,ans,S,T,h,t;
int head[10005],dis[10005],q[800005],nxt[500005];
struct node{
    int to,val;
}L[500005];
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
void add(int x,int y,int c){
    L[cnt]=(node){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(node){x,0};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
int BFS(){
    memset(dis,0,sizeof(dis));
    h=t=0;q[++t]=S;dis[S]=1;
    while(h<t){
        int front=q[++h];
        for(int i=head[front];i!=-1;i=nxt[i]){
            int to=L[i].to;
            if(!dis[to]&&L[i].val){
                dis[to]=dis[front]+1;
                q[++t]=to;
            }
        }
    }
    return dis[T];
}
int DFS(int now,int x){
    if(now==T) return x;
    int res=0;
    for(int i=head[now];i!=-1&&x;i=nxt[i]){
        int to=L[i].to;
        if(dis[to]==dis[now]+1&&L[i].val){
            int fd=DFS(to,min(x,L[i].val));
            res+=fd;x-=fd;
            L[i].val-=fd;L[i^1].val+=fd;
        }
    }
    if(!res) dis[now]=0;
    return res;
}
int main()
{
    n=read();m=read();S=0;T=n*m+1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++) w[i][j]=read()*2,sum+=w[i][j]/2;
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++) l[i][j]=read()*2,sum+=l[i][j]/2;
    for(int i=1;i<n;i++)
     for(int j=1;j<=m;j++){
        int x=read();
        w[i][j]+=x;w[i+1][j]+=x;sum+=x;
        add((i-1)*m+j,i*m+j,x);
        add(i*m+j,(i-1)*m+j,x);
     }
    for(int i=1;i<n;i++)
     for(int j=1;j<=m;j++){
        int x=read();
        l[i][j]+=x;l[i+1][j]+=x;sum+=x;
        add((i-1)*m+j,i*m+j,x);
        add(i*m+j,(i-1)*m+j,x);
     }
    for(int i=1;i<=n;i++)
     for(int j=1;j<m;j++){
        int x=read();
        w[i][j]+=x;w[i][j+1]+=x;sum+=x;
        add((i-1)*m+j,(i-1)*m+j+1,x);
        add((i-1)*m+j+1,(i-1)*m+j,x);
     }
    for(int i=1;i<=n;i++)
     for(int j=1;j<m;j++){
        int x=read();
        l[i][j]+=x;l[i][j+1]+=x;sum+=x;
        add((i-1)*m+j,(i-1)*m+j+1,x);
        add((i-1)*m+j+1,(i-1)*m+j,x);
     }
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++){
        add(S,(i-1)*m+j,w[i][j]);
        add((i-1)*m+j,T,l[i][j]);
     }
    while(BFS()) 
      ans+=DFS(S,2e9);
    printf("%d",sum-(ans/2));
    return 0;
}
3.这题我们不考虑单个点,我们考虑整行整列。很显然,我们要消去一个点,必须消掉那一行,或者一列。所以我们建个图,把行放一边,列放另一边,对于点(x,y),则把行x和y连一条权值为一的边,再把每一行和S相连,每一列和T相连,做一遍最大流,即可。
图为样例建图,十分明显

这里写图片描述

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<string.h>
#include<cmath>
#include<iostream> 
using namespace std;
int n,m,ans,cnt,k,S,T,h,t;
int head[1000],dis[1000],q[400000],nxt[200000];
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
struct node{
    int to,val;
}L[200000];
void add(int x,int y,int c){
    L[cnt].to=y;L[cnt].val=c;
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt].to=x;L[cnt].val=0;
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
int BFS(){
    memset(dis,0,sizeof(dis));
    dis[S]=1;h=0;t=1;q[t]=S;
    while(h<t){
        int front=q[++h];
        for(int i=head[front];i!=-1;i=nxt[i]){
            int to=L[i].to;
            if(!dis[to]&&L[i].val){
                dis[to]=dis[front]+1;
                q[++t]=to;
            }
        }
    }
    return dis[T];
}
int DFS(int now,int x){
    if(now==T) return x;
    int res=0;
    for(int i=head[now];i!=-1&&x;i=nxt[i]){
        int to=L[i].to;
        if(dis[to]==dis[now]+1&&L[i].val){
            int fd=DFS(to,min(x,L[i].val));
            res+=fd;x-=fd;
            L[i].val-=fd;L[i^1].val+=fd;
        }
    }
    if(!res) dis[now]=0;
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    k=read();n=read();S=0;T=2*k+1;
    for(int i=1;i<=n;i++){
        int x=read(),y=read();
        add(x,k+y,1);
    }
    for(int i=1;i<=k;i++) add(S,i,1),add(i+k,T,1); 
    while(BFS()) 
        ans+=DFS(S,2e9);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值