网络流——最大流问题例题

P1231 教辅的组成

https://www.luogu.org/problemnew/show/P1231

这道题不难发现是网络流题,主要要想到怎么建图,然后跑遍网络流就ok了

我们先对练习册和书连一条边容量为1,然后书与答案连一条边容量为1,但是这样有问题,因为书的流量可能会很多,所以我们要把书拆成两堆,然后书一与书二之间连一条边容量为一,然后我们建一个超级源点,超级汇点,连起来,容量都是一,这样建图就好了,然后dinic跑一次,就ok

附上代码

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 #include<queue>
  8 #define ll long long
  9 using namespace std;
 10 #define INF 0x3f3f3f
 11 struct node
 12 {
 13     int from,to,cap,flow;
 14 };
 15 vector<node>edges;
 16 vector<int>load[1000005];
 17 int n,m,s,t;
 18 int cur[1000005];
 19 int d[1000005];
 20 void add(int u,int v,int w)
 21 {
 22     edges.push_back((node){u,v,w,0});
 23     edges.push_back((node){v,u,0,0});
 24     int x=edges.size();
 25     load[u].push_back(x-2);
 26     load[v].push_back(x-1);
 27 }
 28 bool bfs()
 29 {
 30     memset(d,0,sizeof(d));
 31     queue<int>q;
 32     q.push(s);
 33     d[s]=1;
 34     while(!q.empty())
 35     {
 36         int u=q.front();q.pop();
 37         int x=load[u].size();
 38         for(int i=0;i<x;i++)
 39         {
 40             node &v=edges[load[u][i]];
 41             if(!d[v.to]&&v.cap>v.flow)
 42             {
 43                 d[v.to]=d[u]+1;
 44                 q.push(v.to);
 45             }
 46         }
 47     }
 48     return d[t]!=0?1:0;
 49 }
 50 int dfs(int u,int mini)
 51 {
 52     if(mini==0||u==t)
 53     return mini;
 54     int x=load[u].size();
 55     int flow=0;
 56     for(int &i=cur[u];i<x;i++)
 57     {
 58         node& v=edges[load[u][i]];
 59         int f;
 60         if(d[v.to]==d[u]+1&&(f=dfs(v.to,min(mini,v.cap-v.flow)))>0)
 61         {
 62             v.flow+=f;
 63             edges[load[u][i]^1].flow-=f;
 64             flow+=f;
 65             mini-=f;
 66             if(mini==0)break;
 67         }
 68     }
 69     return flow;
 70 }
 71 void dinic()
 72 {
 73     int ans=0;
 74     while(bfs())
 75     {
 76         memset(cur,0,sizeof(cur));
 77         ans+=dfs(s,INF);
 78     }
 79     printf("%d",ans);
 80 }
 81 int n1,n2,n3,m1,m2,m3;
 82 int main()
 83 {
 84     s=0;
 85     scanf("%d %d %d",&n1,&n2,&n3);
 86     scanf("%d",&m1);
 87     for(int i=1;i<=m1;i++)
 88     {
 89         int x,y;
 90         scanf("%d %d",&x,&y);
 91         add(y,x+n2,1);
 92     }
 93     scanf("%d",&m2);
 94     for(int i=1;i<=m2;i++)
 95     {
 96         int x,y;
 97         scanf("%d %d",&x,&y);
 98         add(x+n2+n1,2*n1+n2+y,1);
 99     }
100     for(int i=1;i<=n2;i++)
101     {
102         add(0,i,1);
103     }
104     for(int i=1;i<=n1;i++)
105     {
106         add(n2+i,n2+n1+i,1);
107     }
108     int end=n1+n2*2+n3+1;
109     t=end;
110     for(int i=1;i<=n1;i++)
111     {
112         add(n2+2*n1+i,end,1);
113     }
114     dinic();
115 }
View Code

 

P3324 [SDOI2015]星际战争

https://www.luogu.org/problemnew/show/P3324

这道题只要想到怎么建图,然后二分枚举时间,就ok了,一定要考虑精度问题!!

首先建立一个超级源点S和超级汇点T,将每个机器人跟汇点相连,容量为机器人的护甲值,然后激光器跟机器人相连,容量为INF(极大的数),

最后用二分的时间time*b[i](b[i]第i个激光每秒的伤害)的值当作容量将源点与第i个激光器相连,跑遍dinic,判断是否成立,不成立就接着二分

附上代码

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<vector>
  7 #include<queue>
  8 #include<ctime>
  9 #include<cmath>
 10 #define MAXN 60000
 11 #define INF 21474873647990000
 12 #define LL long long
 13 using namespace std;
 14 LL S,T,a[MAXN],b[MAXN],c[1001][1001];
 15 LL n,m,cnt,head[MAXN<<1],leve[MAXN<<1];
 16 LL sum;
 17 
 18 inline LL read(){
 19     LL x=0,f=1; char ch=getchar();
 20     for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
 21     for(;isdigit(ch);ch=getchar())x=ch-'0'+(x<<3)+(x<<1);
 22     return x*f;
 23 }
 24 
 25 struct node{
 26     LL to,next;
 27     LL f;
 28 }w[MAXN<<2];
 29 
 30 void add(LL u,LL v,LL fl){
 31     cnt++;
 32     w[cnt].to=v;w[cnt].f=fl;w[cnt].next=head[u];head[u]=cnt;
 33     cnt++;
 34     w[cnt].to=u;w[cnt].f=0; w[cnt].next=head[v];head[v]=cnt;
 35 }
 36 
 37 queue<LL>q;
 38 
 39 bool bfs(){
 40     memset(leve,-1,sizeof(leve));
 41     q.push(S);
 42     leve[S]=0;
 43     while(!q.empty()){
 44         LL ans=q.front();
 45         q.pop();
 46         for(LL i=head[ans];i!=-1;i=w[i].next){
 47             if(leve[w[i].to]==-1 && w[i].f){
 48                 leve[w[i].to]=leve[ans]+1;
 49                 q.push(w[i].to);
 50             }
 51         }
 52     }
 53     if(leve[T]==-1)return 0;
 54     return 1;
 55 }
 56 
 57 
 58 LL dfs(LL s,LL f){
 59     if(s==T || f==0) return f;
 60     LL tmp=0;
 61     for(LL i=head[s];i!=-1 && tmp<f;i=w[i].next){
 62         LL fl=w[i].f;
 63         if(w[i].f!=0 && leve[w[i].to]==leve[s]+1){
 64             fl=dfs(w[i].to,min(f-tmp,fl));
 65             w[i].f-=fl;
 66             w[i^1].f+=fl;
 67             tmp+=fl;
 68         }
 69         if(tmp==f)return f;
 70     }
 71     if(tmp==0)leve[s]=-1;
 72     return tmp;
 73 }
 74 LL dinic(LL mid){
 75     cnt=-1;
 76     memset(head,-1,sizeof(head));
 77     memset(w,0,sizeof(w));
 78     for(int i=1;i<=n;i++)add(i+m,T,a[i]);
 79     for(int i=1;i<=m;i++)add(S,i,b[i]*mid);
 80     for(int i=1;i<=m;i++){
 81         for(int j=1;j<=n;j++){
 82             if(c[i][j])add(i,j+m,INF);
 83         }
 84     }
 85     LL ans=0;
 86     while(bfs()){
 87         ans+=dfs(S,INF);
 88     }
 89     return ans;
 90     
 91 }
 92 int main(){
 93     n=read();
 94     m=read();
 95     S=0;
 96     T=n+m+1;
 97     for(int i=1;i<=n;i++){
 98         a[i]=read()*1000;
 99         sum+=a[i];
100     }
101     for(int i=1;i<=m;i++)b[i]=read();
102     for(int i=1;i<=m;i++){
103         for(int j=1;j<=n;j++)c[i][j]=read();
104     }
105     LL x=0,y=5000000;
106     while(x<=y){
107         LL mid=(x+y)>>1,ans=dinic(mid);
108         if(ans==sum)y=mid-1;
109         else x=mid+1;
110     }
111     printf("%.6f",y/1000.0);
112     return 0;
113 }
View Code

 

P2774 方格取数问题

https://www.luogu.org/problemnew/show/P2774

相邻的数字只能选一个,联想到二分图,相邻的点分别放在x、y端,他们之间连一条边,为什么他们之间连一条边呢,因为相邻数字的关系有且仅有相邻为影响,我们需要把影响转化到模型上,

不相邻的点建边虽然思考起来更直观,但是边数太多了,反而更难理清思路,要使边有意义且合法,我们需要构造起始点和终点,因为x的每个点地位相同,那么都和s点相连,同理y和t相连

显然对于构造出来的这个图,我们要尝试把题意转化进去,即答案需要的是一种什么图。x和y相连的两个点只能要一个,这是题意,那么结果图中不能有s-u-v-t完整的4条弧构成的“链”,

断链的方法显然为最小割,最小割最大流是很重要的定理。模拟一下最大流的过程,举几个小例子就能明白我们割掉的是小的,留下来的是大的和小的的差那么结果就显而易见了。

 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <queue>
 8 #define INF 21474836
 9 using namespace std;
10 
11 struct node{
12     int to,next,f;
13 }w[1000001];
14 int sum=-1,num[101][101],mp[101][101],head[100001],p[100001];
15 
16 void add(int u,int v,int fl){
17     sum++;
18     w[sum].to=v;w[sum].next=head[u];w[sum].f=fl;head[u]=sum;
19     sum++;
20     w[sum].to=u;w[sum].next=head[v];w[sum].f=0; head[v]=sum;
21 }
22 queue<int>q;
23 bool bfs(int s,int t){
24     memset(p,-1,sizeof(p));
25     q.push(s); p[s]=0;
26     while(!q.empty()){
27         int ans=q.front(); q.pop();
28         for(int i=head[ans];i!=-1;i=w[i].next){
29             if(p[w[i].to]==-1 && w[i].f!=0){
30                 p[w[i].to]=p[ans]+1;
31                 q.push(w[i].to);
32             }
33         }
34         
35     }
36     if(p[t]==-1)return 0;
37     return 1;
38 }
39 int dfs(int s,int t,int maxf){
40     if(s==t||maxf==0)return maxf;
41     int tmp=0;
42     for(int i=head[s];i!=-1 && tmp<maxf;i=w[i].next){
43         int f=w[i].f;
44         if(p[w[i].to]==p[s]+1 && f!=0){
45             f=dfs(w[i].to,t,min(maxf-tmp,f));
46             w[i].f-=f;
47             w[i^1].f+=f;
48             tmp+=f;
49         }
50     }
51     if(tmp==0)p[s]=-1;
52     return tmp;
53 }
54 int dinic(int s,int t){
55     int num=0;
56     while(bfs(s,t)){
57         num+=dfs(s,t,INF);
58     }
59     return num;
60 }
61 
62 int main(){
63     int m,n,s,t,tot,ans=0,x,y;
64     memset(head,-1,sizeof(head));
65     scanf("%d%d",&n,&m);
66     s=n*m;
67     t=n*m+1;
68     tot=n*m+2;
69     x=0;
70     for(int i=0;i<n;i++){
71         for(int j=0;j<m;j++){
72             if (i%2==0) num[i][j]=x++;
73             else num[i][m-j-1]=x++;
74         }
75     }
76     for(int i=0;i<n;i++){
77         for(int j=0;j<m;j++)scanf("%d",&mp[i][j]);
78     }
79     for(int i=0;i<n;i++){
80         for(int j=0;j<m;j++){
81             ans+=mp[i][j];
82             if (num[i][j]%2==0){
83                 if (i<n-1) add(num[i][j],num[i+1][j],INF);
84                 if (j<m-1) add(num[i][j],num[i][j+1],INF);
85                 if (i>0) add(num[i][j],num[i-1][j],INF);
86                 if (j>0) add(num[i][j],num[i][j-1],INF);
87                 add(s,num[i][j],mp[i][j]);
88             }
89             else add(num[i][j],t,mp[i][j]);
90         }
91     }
92     int maxflow=dinic(s,t);
93     printf("%d\n",ans-maxflow);
94     return 0;
95 }
View Code

 

转载于:https://www.cnblogs.com/wzq--boke/p/8433556.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值