首页 > 技术文章 > 网络流24题! 开始!题解!

xseventh 2017-11-28 21:36 原文

做一做网络流,明年加油!

这里写一下题解,写下每一题我的想法。

如果发现哪里写的不对希望能指出来,谢谢!(嘿嘿~  我觉得可能写的挺对的)

 

问题编号

问题名称

问题模型

转化模型

1

飞行员配对方案问题

二分图最大匹配

网络最大流 

2

太空飞行计划问题

最大权闭合图

网络最小割 

3

最小路径覆盖问题

有向无环图最小路径覆盖

网络最大流     

4

魔术球问题

有向无环图最小路径覆盖

网络最大流 

5

圆桌问题

二分图多重匹配

网络最大流 

6

最长递增子序列问题

最多不相交路径

网络最大流

7

试题库问题

二分图多重匹配

网络最大流      

8

机器人路径规划问题

(未解决)

最小费用最大流

9

方格取数问题

二分图点权最大独立集

网络最小割

10

餐巾计划问题

线性规划网络优化

最小费用最大流

11

航空路线问题

最长不相交路径

最小费用最大流

12

软件补丁问题

最小转移代价

最短路径

13

星际转移问题

网络判定

网络最大流

14

孤岛营救问题

分层图最短路径

最短路径

15

汽车加油行驶问题

分层图最短路径

最短路径

16

数字梯形问题

最大权不相交路径

最小费用最大流

17

运输问题

网络费用流量

最小费用最大流

18

分配问题

二分图最佳匹配

最小费用最大流

19

负载平衡问题

最小代价供求

最小费用最大流

20

深海机器人问题

线性规划网络优化

最小费用最大流

21

最长k可重区间集问题

最大权不相交路径

最小费用最大流

22

最长k可重线段集问题

最大权不相交路径

最小费用最大流

23

火星探险问题

线性规划网络优化

最小费用最大流

24

骑士共存问题

二分图最大独立集

网络最小割

1、飞行员配对方案问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1736

简单的建图,二分图匹配也能做

下面是代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #define inf 0x3fffffff
  6 using namespace std;
  7 const int maxn=555;
  8 int first[maxn],sign,cur[maxn];
  9 int s,t,d[maxn];
 10 int mp[maxn][maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 }edge[maxn*50];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26  
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top];~i;i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top];~i;i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0;i<=n;i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int main()
 92 {
 93     int n,m;
 94     scanf("%d%d",&m,&n);
 95     creat();
 96     s=0,t=n+m+1;
 97     for(int i=1;i<=m;i++)
 98         add_edge(s,i,1);
 99     for(int i=m+1;i<=n+m;i++)
100         add_edge(i,t,1);
101     int x,y;
102     while(1)
103     {
104         scanf("%d%d",&x,&y);
105         if(x==-1&&y==-1)break;
106         add_edge(x,y,1);
107     }
108     int ans=dinic(t);
109     printf("%d\n",ans);
110     if(ans==0)
111     {
112         printf("No Solution!\n");
113         return 0;
114     }
115     for(int i=m+1;i<=n+m;i++)
116         for(int j=first[i];~j;j=edge[j].next)
117         if(edge[j].to!=t&&edge[j].w==1)
118         printf("%d %d\n",edge[j].to,i);
119     return 0;
120 }

 

2、太空飞行计划问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1737

 最大权闭合图模型题,最大流最小割。

从起点往每个权值为正的点建立一条边,容量为点权值。

每个权值为负的点往终点建立一条边,容量为权值的绝对值。

如果选A就必须选B 则就从A建立一条往B的边,容量为正无穷。

然后正权值加起来减去最大流就是能选出来最大权闭合图所有点加起来的值

最大权闭合图的点就是从起点开始广搜,权值为0的点不走,能走到的点就是被选中的点。

dinic最后一次bfs的d数组正好可以用来判断这个条件。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=555;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*50];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26   
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91  
 92 int main()
 93 {
 94     int n,m;
 95     scanf("%d%d",&m,&n);
 96     creat();
 97     s=0,t=n+m+1;
 98     int sum=0;
 99     for(int i=1; i<=m; i++)
100     {
101         int x;
102         char c;
103         scanf("%d",&x);
104         sum+=x;
105         add_edge(s,n+i,x);
106         while(1)
107         {
108             scanf("%d%c",&x,&c);
109             add_edge(n+i,x,inf);
110             if(c=='\n'||c=='\r')break;
111         }
112     }
113     for(int i=1; i<=n; i++)
114     {
115         int w;
116         scanf("%d",&w);
117         add_edge(i,t,w);
118     }
119     sum-=dinic(t);
120   
121     for(int i=1; i<=m; i++)
122     {
123         if(d[i+n])
124             printf("%d ",i);
125     }
126     printf("\n");
127     for(int i=1; i<=n; i++)
128         if(d[i])
129             printf("%d ",i);
130     printf("\n");
131     printf("%d\n",sum);
132     return 0;
133 }

 

3、最小路径覆盖问题

题目链接:https://loj.ac/problem/6002

最小路径覆盖问题模型,拆点做最大匹配,总点数减去最大流为需要的最少的边数

题目有建图方法

拆点意思是这样的,第i个点的流量流向第j个点时表示第i个点的下一个点是j点,剩下的没办法流向终点的点就是没有点能到它那里,说明这个点是边的起点,根据这个来递归可以用来查找答案方案。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=555;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*50];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26 
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int n,m;
 92 int vis[maxn];
 93 int go(int x,int &f)
 94 {
 95     int loc=x+n;
 96     vis[x]=1;
 97     for(int i=first[loc]; ~i; i=edge[i].next)
 98         if(edge[i].w==1&&edge[i].to!=n*2+1)
 99             go(edge[i].to,f);
100     if(f==1)f=0;
101     else putchar(' ');
102     printf("%d",x);
103 }
104 int main()
105 {
106     scanf("%d%d",&n,&m);
107     creat();
108     s=0,t=n*2+1;
109     for(int i=1; i<=n; i++)
110         add_edge(s,i,1),add_edge(i+n,t,1);
111     int x,y;
112     while(m--)
113         scanf("%d%d",&x,&y),add_edge(x,y+n,1);
114     int ans=n-dinic(t);
115     for(int i=first[t]; ~i; i=edge[i].next)
116         if(edge[i].w==1&&!vis[edge[i].to-n])
117         {
118             int f=1;
119             go(edge[i].to-n,f);
120             puts("");
121         }
122     for(int i=1;i<=n;i++)
123         if(!vis[i])printf("%d\n",i);
124     printf("%d\n",ans);
125     return 0;
126 }

 

4、魔术球问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1739

每个点能连接的下一个点可以连成一个链,给你1-m个点,求出来的最小路径覆盖的答案就是需要的桩子个数(画重点!最小路径覆盖)

因为网络流是可以在残量网络上继续跑的,所以我们每次就多增加一个点的边,然后一直跑网络流,直到需要的桩子个数刚好超过题目给的桩子个数时就是最多的球的个数了,我们再按照上面那题的方法递归输出答案即可。

  1 #include<cstdio>
  2 #include<cmath>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<queue>
  7 #define inf 0x3fffffff
  8 using namespace std;
  9 const int maxn=11111;
 10 int first[maxn],sign,cur[maxn];
 11 int s,t,d[maxn];
 12 struct node
 13 {
 14     int to,w,next;
 15 } edge[maxn*20];
 16 void creat()
 17 {
 18     memset(first,-1,sizeof(first));
 19     sign=0;
 20 }
 21 void add_edge(int u,int v,int w)
 22 {
 23     edge[sign].to=v;
 24     edge[sign].w=w;
 25     edge[sign].next=first[u];
 26     first[u]=sign++;
 27  
 28     edge[sign].to=u;
 29     edge[sign].w=0;
 30     edge[sign].next=first[v];
 31     first[v]=sign++;
 32 }
 33 int bfs()
 34 {
 35     queue<int>q;
 36     memset(d,0,sizeof(d));
 37     d[s]=1;
 38     q.push(s);
 39     while(!q.empty())
 40     {
 41         int top=q.front();
 42         q.pop();
 43         for(int i=first[top]; ~i; i=edge[i].next)
 44         {
 45             int to=edge[i].to;
 46             if(edge[i].w>0&&d[to]==0)
 47             {
 48                 d[to]=d[top]+1;
 49                 if(to==t)
 50                     return 1;
 51                 q.push(to);
 52             }
 53         }
 54     }
 55     return d[t]!=0;
 56 }
 57 int dfs(int top,int flow)
 58 {
 59     if(top==t)
 60         return flow;
 61     int ans=0,x=0;
 62     for(int i=cur[top]; ~i; i=edge[i].next)
 63     {
 64         int to=edge[i].to;
 65         if(edge[i].w>0&&d[to]==d[top]+1)
 66         {
 67             x=dfs(to,min(flow-ans,edge[i].w));
 68             edge[i].w-=x;
 69             edge[i^1].w+=x;
 70             if(edge[i].w)
 71                 cur[top] = i;
 72             ans+=x;
 73             if(ans==flow)
 74                 return flow;
 75         }
 76     }
 77     if(ans==0)
 78         d[top]=0;
 79     return ans;
 80 }
 81 int dinic(int n)
 82 {
 83     int ans=0;
 84     while(bfs())
 85     {
 86         for(int i=0; i<=n; i++)
 87             cur[i]=first[i];
 88         ans+=dfs(s,inf);
 89     }
 90     return ans;
 91 }
 92 int id[11111],wid[11111];
 93 int vis[maxn];
 94 int go(int x,int &f)
 95 {
 96     int loc=wid[x];
 97     vis[x]=1;
 98     for(int i=first[loc]; ~i; i=edge[i].next)
 99         if(edge[i].w==1&&edge[i].to!=t)
100             go(edge[i].to/2,f);
101     if(f==1)f=0;
102     else putchar(' ');
103     printf("%d",x);
104 }
105  
106 int main()
107 {
108     creat();
109     int n;
110     scanf("%d",&n);
111     s=0,t=11110;
112     int cnt=2;
113     int ans=0,i=0;
114     for(i=1; i-ans<=n+1; i++)
115     {
116         id[i]=cnt++;
117         wid[i]=cnt++;
118         add_edge(s,id[i],1);
119         add_edge(wid[i],t,1);
120         for(int j=1; j<i; j++)
121         {
122             int se=sqrt(i+j);
123             if(se*se==i+j)
124             {
125                 add_edge(id[j],wid[i],1);
126             }
127         }
128         ans+=dinic(t);
129     }
130     int as=i-2;
131     printf("%d\n",i-2);
132     for(i=first[t]; ~i; i=edge[i].next)
133         if(edge[i].w==1&&!vis[edge[i].to/2])
134         {
135             int f=1;
136             go(edge[i].to/2,f);
137             puts("");
138         }
139     for(i=1;i<=as;i++)
140     if(!vis[i])printf("%d\n",i);
141     return 0;
142 }

 

5、圆桌问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1740

这个挺水的,感觉就是暴力建图跑个最大流就可以了。

起点往每个单位的点建立一个边,边的容量就是单位的人数。

每个单位对每个餐桌拆一个点出来限制一下流量,让每个单位去每个餐桌只能有1的流量。

然后每个餐桌往终点建立一个边,容量为餐桌的容量。

这样跑个最大流,然后看结果是不是和单位人数相同就行,不同的话就不可行。

输出答案的话就找那个限制流量的边,如果那个边走过了就说明这个点有用嘛,找一下输出就行。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=51111;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*20];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26  
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 vector<int>as[maxn];
 92 int main()
 93 {
 94     creat();
 95     int n,m,sum=0;
 96     scanf("%d%d",&n,&m);///n 单位数  m餐桌数
 97     s=0;t=n+m+n*m+1;
 98     for(int i=1,x;i<=n;i++)
 99         scanf("%d",&x),add_edge(s,i,x),sum+=x;
100     for(int i=1;i<=n;i++)
101         for(int j=1;j<=m;j++)
102         add_edge(i,n+(i-1)*m+j,1),add_edge(n+(i-1)*m+j,j+n+n*m,1);
103     for(int i=1,x;i<=m;i++)
104         scanf("%d",&x),add_edge(i+n+n*m,t,x);
105     if(dinic(t)==sum)
106         puts("1");
107     else
108     {
109         puts("0");
110         return 0;
111     }
112     for(int i=1;i<=m;i++)
113     {
114         int f=1;
115         for(int j=first[i+n+n*m];~j;j=edge[j].next)
116         if(edge[j].to!=t&&edge[j].w==1)
117         {
118             as[(edge[j].to-n-i)/m+1].push_back(i);
119         }
120     }
121     for(int i=1;i<=n;i++)
122     {
123         printf("%d",as[i][0]);
124         for(int j=1;j<as[i].size();j++)
125             printf(" %d",as[i][j]);
126         printf("\n");
127     }
128     return 0;
129 }

 

6、最长递增子序列问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1741

这题第一问随便求,n方或者nlogn的dp都可以,难点在第二问和第三问,一开始我没注意到第二问和第三问的询问s是第一问的答案,以为是随便询问一个长度,后来发现是求第一问的答案,这样就简单点了。

建图方案:

把每个点拆成两个点,拆点是为了让每个点只走一次。

起点每次连到dp出来答案等于s的点,容量为1。

然后dp长度为1的点连到终点,容量也是1。

具体的不好说,看我代码吧,i和i+n是一对点,跑出来的最大流就直接是答案。

因为拆点是为了限制每个点只用一次,所以第三问就把1这个点的能用次数改成无穷大,最后一个点也是,然后再跑一下最大流就好。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=888;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*20];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26 
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int a[maxn],f[maxn];
 92 int main()
 93 {
 94     creat();
 95     int n;
 96     scanf("%d",&n);
 97     for(int i=1;i<=n;i++)
 98         scanf("%d",a+i),f[i]=1;
 99     for(int i=1;i<=n;i++)
100         for(int j=1;j<i;j++)
101         if(a[i]>a[j])f[i]=max(f[i],f[j]+1);
102     int ans=0;
103     for(int i=1;i<=n;i++)
104         ans=max(ans,f[i]);
105     s=0,t=n*2+1;
106     for(int i=1;i<=n;i++)
107         if(f[i]==ans)
108         add_edge(s,i,1);
109     for(int i=1;i<=n;i++)
110         add_edge(i+n,i,1);
111     for(int i=1;i<=n;i++)
112         if(f[i]==1)
113         add_edge(i,t,1);
114     for(int i=1;i<=n;i++)
115         for(int j=1;j<i;j++)
116             if(a[i]>a[j]&&f[i]==f[j]+1)
117             add_edge(i,j+n,1);
118     int sum=dinic(t);
119     printf("%d\n%d\n",ans,sum);
120     add_edge(1+n,1,inf);
121     add_edge(1,t,inf);
122     if(f[n]==ans)
123         add_edge(s,n,inf);
124     sum+=dinic(t);
125     printf("%d\n",sum);
126     return 0;
127 }

 

7、试题库问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1742

建图,就是普通的匹配吧,起点往每个类型建立边,容量为需要的这个类型的试题数量。

然后对每个关系建立一条边,然后把每个题目和终点再连一条边,容量为1,跑出来的最大流就是答案。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=1777;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*20];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26  
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int a[maxn],f[maxn];
 92 int main()
 93 {
 94     creat();
 95     int k,n;
 96     int sum=0;
 97     scanf("%d%d",&k,&n);
 98     s=0,t=n+k+1;
 99     for(int i=1;i<=n;i++)
100         add_edge(i,t,1);
101     for(int i=1,x;i<=k;i++)
102         scanf("%d",&x),add_edge(s,n+i,x),sum+=x;
103     for(int i=1;i<=n;i++)
104     {
105         int m,x;
106         scanf("%d",&m);
107         while(m--)
108         {
109             scanf("%d",&x);
110             add_edge(n+x,i,1);
111         }
112     }
113     if(dinic(t)!=sum)
114     {
115         printf("No Solution!\n");
116         return 0;
117     }
118     for(int i=1;i<=k;i++)
119     {
120         printf("%d:",i);
121         for(int j=first[n+i];~j;j=edge[j].next)
122         if(edge[j].to!=s&&!edge[j].w)
123         {
124             printf(" %d",edge[j].to);
125         }
126         printf("\n");
127     }
128     return 0;
129 }

 

8、机器人路径规划问题

太难了,不会,跳过,而且网上说这题不是网络流题。

 

 

9、方格取数问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1744

建图,把图分成两部分,变成二分图,然后找出最小割,用所有的和减去最小割就是答案。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=2777;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*20];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26  
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int a[111][111];
 92 int go[4][2]= {1,0,0,1,-1,0,0,-1};
 93 int main()
 94 {
 95     int n,m;
 96     scanf("%d%d",&n,&m);
 97     creat();
 98     int sum=0;
 99     for(int i=1; i<=n; i++)
100         for(int j=1; j<=m; j++)
101             scanf("%d",&a[i][j]),sum+=a[i][j];
102     s=0,t=n*m+1;
103     for(int i=1; i<=n; i++)
104         for(int j=1; j<=m; j++)
105             if((i+j)&1)
106                 add_edge(s,(i-1)*m+j,a[i][j]);
107             else add_edge((i-1)*m+j,t,a[i][j]);
108     for(int i=1; i<=n; i++)
109         for(int j=1; j<=m; j++)
110         if((i+j)&1)
111             for(int k=0; k<4; k++)
112             {
113                 int x=i+go[k][0],y=j+go[k][1];
114                 if(x>=1&&x<=n&&y>=1&&y<=m)
115                 {
116                     int res=(x-1)*m+y;
117                     add_edge((i-1)*m+j,res,inf);
118                 }
119             }
120     printf("%d\n",sum-dinic(t));
121     return 0;
122 }

 

10、餐巾计划问题

 题目链接:https://www.oj.swust.edu.cn/problem/show/1745

这个题是个费用流,建图比较神奇,我一开始想了好久,就是不知道该怎么建图,当时感觉就光让我跑最大流我都不一定会跑。后来乱画画好像画出来了?(一脸懵逼

建图大概是这样,s为起点,t为终点,然后s连接到 i 有一条容量为a[i],费用为0的边,这个流量是今天用完了的毛巾,然后s连接到 i' 有一条容量为a[i],费用为p的边,然后 i' 到t又有一条容量为a[i],费用为0的边。然后剩下的按照题意加几个转移的边就好了。

分析一下:因为有一条s-i'-t的路径,容量都是a[i],所以i'到t一定是满流的,说明今天一定会用a[i]条毛巾。所以s-i每次流过去的a[i]就是今天用完剩下来的毛巾(精髓),这样的话i'到t的流量有两种来源:

1、用p的价格来买的毛巾

2、前几天的剩下的毛巾转移过来的流量

然后一波转移猛如虎这个题就能A了。具体实现看代码吧,感觉还是很难想的

洛谷上数据比较强 要开long long

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <string>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <queue>
  8 #include <map>
  9 #include <set>
 10 #define eps 1e-5
 11 #define MAXN 2555
 12 #define MAXM 55555
 13 #define INF 1000000007
 14 using namespace std;
 15 struct EDGE
 16 {
 17     int cost, cap, v;
 18     int next, re;
 19 }edge[MAXM];
 20 int head[MAXN], e;
 21 int vis[MAXN];
 22 int ans, cost, st, ed, maxflow;
 23 void creat()
 24 {
 25     memset(head, -1, sizeof(head));
 26     e = 0;
 27     ans = cost = maxflow = 0;
 28 }
 29 void add_edge(int u, int v, int cap, int cost)
 30 {
 31     edge[e].v = v;
 32     edge[e].cap = cap;
 33     edge[e].cost = cost;
 34     edge[e].re = e + 1;
 35     edge[e].next = head[u];
 36     head[u] = e++;
 37     edge[e].v = u;
 38     edge[e].cap = 0;
 39     edge[e].cost = -cost;
 40     edge[e].re = e - 1;
 41     edge[e].next = head[v];
 42     head[v] = e++;
 43 }
 44 int aug(int u, int f)
 45 {
 46     if(u == ed)
 47     {
 48         ans += cost * f;
 49         maxflow += f;
 50         return f;
 51     }
 52     vis[u] = 1;
 53     int tmp = f;
 54     for(int i = head[u]; i != -1; i = edge[i].next)
 55         if(edge[i].cap && !edge[i].cost && !vis[edge[i].v])
 56         {
 57             int delta = aug(edge[i].v, tmp < edge[i].cap ? tmp : edge[i].cap);
 58             edge[i].cap -= delta;
 59             edge[edge[i].re].cap += delta;
 60             tmp -= delta;
 61             if(!tmp) return f;
 62         }
 63     return f - tmp;
 64 }
 65 bool modlabel(int n)
 66 {
 67     int delta = INF;
 68     for(int u = 1; u <= n; u++)
 69         if(vis[u])
 70             for(int i = head[u]; i != -1; i = edge[i].next)
 71                 if(edge[i].cap && !vis[edge[i].v] && edge[i].cost < delta) delta = edge[i].cost;
 72     if(delta == INF) return false;
 73     for(int u = 1; u <= n; u++)
 74         if(vis[u])
 75             for(int i = head[u]; i != -1; i = edge[i].next)
 76                 edge[i].cost -= delta, edge[edge[i].re].cost += delta;
 77     cost += delta;
 78     return true;
 79 }
 80 int costflow(int n)
 81 {
 82     do
 83     {
 84         do
 85         {
 86             memset(vis, 0, sizeof(vis));
 87         }while(aug(st, INF));
 88     }while(modlabel(n));
 89     return ans;
 90 }
 91 int a[MAXN];
 92 int main()
 93 {
 94     int N,p,m,f,n,s;
 95     creat();
 96     scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&s);
 97     for(int i=1;i<=N;i++)
 98         scanf("%d",a+i);
 99     st=N*2+1,ed=N*2+2;
100     for(int i=1;i<=N;i++)
101     {
102         add_edge(st,i,a[i],0);
103         add_edge(st,i+N,a[i],p);
104         if(i<N)
105         add_edge(i,i+1,INF,0);
106         if(i+m<=N)
107         add_edge(i,i+N+m,INF,f);
108         if(i+n<=N)
109         add_edge(i,i+N+n,INF,s);
110         add_edge(i+N,ed,a[i],0);
111     }
112     printf("%d\n",costflow(ed));
113     return 0;
114 }

 

 

11、航空路线问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1746

建图,一个简单的费用流,拆点限制每个点只能走一次,然后费用用负数,跑个最大费用最大流就好了。

题目有个坑,1-n可以直接走且最大流小于2的时候需要你手动特判一下答案,其他的就没啥问题了。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cmath>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <ctime>
  7 #include <map>
  8 #include <queue>
  9 #include <cstdlib>
 10 #include <string>
 11 #include <climits>
 12 #include <set>
 13 #include <vector>
 14 #include <unordered_map>
 15 using namespace std;
 16 unordered_map<string,int>mp;
 17 unordered_map<int,string>mps;
 18 const int MAXN=201;
 19 const int MAXM=2001;
 20 bool vis[MAXN];int dist[MAXN];
 21 int s,t,ans;
 22   
 23 int first[MAXN],sign;
 24 struct EDGE
 25 {
 26     int p,c,cc,next;
 27 }edge[MAXM];
 28   
 29 void creat()
 30 {
 31     memset(first,-1,sizeof(first));
 32     sign=ans=0;
 33 }
 34   
 35 void add_edge(int from,int to,int c,int cost)
 36 {
 37     edge[sign].p=to;
 38     edge[sign].c=c;
 39     edge[sign].cc=cost;
 40     edge[sign].next=first[from];
 41     first[from]=sign++;
 42     edge[sign].p=from;
 43     edge[sign].c=0;
 44     edge[sign].cc=-cost;
 45     edge[sign].next=first[to];
 46     first[to]=sign++;
 47 }
 48   
 49 inline bool spfa(int n,int s,int t){
 50     memset(vis,0,sizeof vis);
 51     for(int i=0;i<=n;i++)dist[i]=1e9;dist[t]=0;vis[t]=1;
 52     deque<int>q;q.push_back(t);
 53     while(!q.empty()){
 54         int now=q.front();q.pop_front();
 55         for(int k=first[now];k>-1;k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc){
 56             dist[edge[k].p]=dist[now]-edge[k].cc;
 57             if(!vis[edge[k].p]){
 58                 vis[edge[k].p]=1;
 59                 if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p);else q.push_back(edge[k].p);
 60             }
 61         }
 62         vis[now]=0;
 63     }
 64     return dist[s]<1e9;
 65 }
 66 inline int dfs(int x,int low){
 67     if(x==t){vis[t]=1;return low;}
 68     int used=0,a;vis[x]=1;
 69     for(int k=first[x];k>-1;k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p]){
 70         a=dfs(edge[k].p,min(edge[k].c,low-used));
 71         if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a;
 72         if(used==low)break;
 73     }
 74     return used;
 75 }
 76 inline int costflow(int n){
 77     int flow=0;
 78     while(spfa(n,s,t)){
 79         vis[t]=1;
 80         while(vis[t]){
 81             memset(vis,0,sizeof vis);
 82             flow+=dfs(s,1e9);
 83         }
 84     }
 85     return flow;
 86 }
 87 int vs[222];
 88 char tmp[22],tmp2[22];
 89 int main()
 90 {
 91     creat();
 92     int n,m;
 93     scanf("%d%d",&n,&m);
 94     for(int i=1; i<=n; i++)
 95     {
 96         scanf("%s",tmp);
 97         mp[tmp]=i;
 98         mps[i]=tmp;
 99     }
100     s=1,t=n*2;
101     for(int i=2;i<n;i++)
102         add_edge(i,i+n,1,-1);
103     add_edge(1,1+n,2,-1);
104     add_edge(n,n+n,2,-1);
105     int f=0;
106     while(m--)
107     {
108         scanf("%s%s",tmp,tmp2);
109         int x=mp[tmp],y=mp[tmp2];
110         if(x>y)swap(x,y);
111         if(x==1&&y==n)f=1;
112         add_edge(x+n,y,1,0);
113     }
114     int maxf=costflow(t);
115     if(f==1&&maxf==1)
116     {
117         printf("2\n");
118         cout<<mps[1]<<endl<<mps[n]<<endl<<mps[1]<<endl;
119         return 0;
120     }
121     if(maxf<2)
122     {
123         printf("No Solution!\n");
124         return 0;
125     }
126   
127     printf("%d\n",-ans-2);
128     int now=1+n;
129     while(now!=n+n)
130     {
131         cout<<mps[now-n]<<endl;
132         for(int i=first[now];~i;i=edge[i].next)
133             if(edge[i].p!=now-n&&edge[i].c==0)
134             {
135                 now=edge[i].p+n;
136                 vs[edge[i].p]=1;
137                 break;
138             }
139     }
140     now-=n;
141     while(now!=1)
142     {
143         cout<<mps[now]<<endl;
144         for(int i=first[now];~i;i=edge[i].next)
145             if(edge[i].c)
146                 if(!vs[edge[i].p-n])
147                     now=edge[i].p-n;
148     }
149     cout<<mps[1]<<endl;
150     return 0;
151 }

 

12、软件补丁问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1747

这题状态压缩跑个最短路就好了,你们感受一下就懂了233

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int inf=1e9;
const int maxn=1<<20;
int n,m,dij[maxn],vis[maxn];
int b1[111],b2[111],f1[111],f2[111],w[111];
int dijstra(int s,int t)
{
    priority_queue<pair<int,int> >q;
    int i;
    for(i=0; i<maxn; i++)dij[i]=inf;
    dij[s]=0;
    q.push(make_pair(-dij[s],s));
    while(!q.empty())
    {
        int now=q.top().second;
        q.pop();
        if(!vis[now])
        {
            if(now==t)return dij[t];
            vis[now]=1;
            for(i=1;i<=m;i++)
                if((now&b1[i])==b1[i]&&(now&b2[i])==0)
            {
                int to=now;
                to&=(~f1[i]);
                to|=f2[i];
                if(dij[to]>dij[now]+w[i])
                {
                    dij[to]=dij[now]+w[i];
                    q.push(make_pair(-dij[to],to));
                }
            }
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        char tmp[22];
        scanf("%d%s",w+i,tmp);
        for(int k=0;tmp[k];k++)
        {
            if(tmp[k]=='+')
                b1[i]|=1<<k;
            else if(tmp[k]=='-')
                b2[i]|=1<<k;
        }
        scanf("%s",tmp);
        for(int k=0;tmp[k];k++)
        {
            if(tmp[k]=='-')
                f1[i]|=1<<k;
            else if(tmp[k]=='+')
                f2[i]|=1<<k;
        }
    }
    printf("%d\n",dijstra((1<<n)-1,0));
}

 

13、星际转移问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1748

建图:这题数据比较小,可以暴力一点建图(一开始怕超时都没敢写   结果意外的跑的挺快)

对每一秒都建一层图,然后正常的转移就好了,每次都往后一秒转移,每一层都有一个节点连接到终点,大概就是这样。

然后题目还要判断走不到的情况,这个就是判断两点能否到达嘛,并查集一下就好了,看地球和月亮是否被并在了一起就行。

代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #define inf 0x3fffffff
  7 using namespace std;
  8 const int maxn=8888;
  9 int first[maxn],sign,cur[maxn];
 10 int s,t,d[maxn];
 11 struct node
 12 {
 13     int to,w,next;
 14 } edge[maxn*20];
 15 void creat()
 16 {
 17     memset(first,-1,sizeof(first));
 18     sign=0;
 19 }
 20 void add_edge(int u,int v,int w)
 21 {
 22     edge[sign].to=v;
 23     edge[sign].w=w;
 24     edge[sign].next=first[u];
 25     first[u]=sign++;
 26 
 27     edge[sign].to=u;
 28     edge[sign].w=0;
 29     edge[sign].next=first[v];
 30     first[v]=sign++;
 31 }
 32 int bfs()
 33 {
 34     queue<int>q;
 35     memset(d,0,sizeof(d));
 36     d[s]=1;
 37     q.push(s);
 38     while(!q.empty())
 39     {
 40         int top=q.front();
 41         q.pop();
 42         for(int i=first[top]; ~i; i=edge[i].next)
 43         {
 44             int to=edge[i].to;
 45             if(edge[i].w>0&&d[to]==0)
 46             {
 47                 d[to]=d[top]+1;
 48                 if(to==t)
 49                     return 1;
 50                 q.push(to);
 51             }
 52         }
 53     }
 54     return d[t]!=0;
 55 }
 56 int dfs(int top,int flow)
 57 {
 58     if(top==t)
 59         return flow;
 60     int ans=0,x=0;
 61     for(int i=cur[top]; ~i; i=edge[i].next)
 62     {
 63         int to=edge[i].to;
 64         if(edge[i].w>0&&d[to]==d[top]+1)
 65         {
 66             x=dfs(to,min(flow-ans,edge[i].w));
 67             edge[i].w-=x;
 68             edge[i^1].w+=x;
 69             if(edge[i].w)
 70                 cur[top] = i;
 71             ans+=x;
 72             if(ans==flow)
 73                 return flow;
 74         }
 75     }
 76     if(ans==0)
 77         d[top]=0;
 78     return ans;
 79 }
 80 int dinic(int n)
 81 {
 82     int ans=0;
 83     while(bfs())
 84     {
 85         for(int i=0; i<=n; i++)
 86             cur[i]=first[i];
 87         ans+=dfs(s,inf);
 88     }
 89     return ans;
 90 }
 91 int p[33];
 92 vector<int>v[33];
 93 
 94 int fa[33];
 95 int _find(int x)
 96 {
 97     while(x!=fa[x])x=x[fa]=x[fa][fa];
 98     return x;
 99 }
100 void _union(int x,int y)
101 {
102     int p1=_find(x),p2=_find(y);
103     if(p1==p2)return;
104     fa[p1]=p2;
105 }
106 int main()
107 {
108     creat();
109     int n,m,k;
110     scanf("%d%d%d",&n,&m,&k);
111     n+=2;
112     for(int i=1;i<=n;i++)
113         fa[i]=i;
114     s=0,t=1;
115     int st=n+n-1;
116     add_edge(s,st,k);
117     for(int i=1;i<=m;i++)
118     {
119         scanf("%d",p+i);
120         int r;
121         scanf("%d",&r);
122         while(r--)
123         {
124             int x;
125             scanf("%d",&x);
126             if(x==0)
127                 x=n-1;
128             else if(x==-1)
129                 x=n;
130             v[i].push_back(x);
131             if(v[i].size()>1)
132                 _union(v[i][v[i].size()-2],v[i][v[i].size()-1]);
133         }
134     }
135     if(k!=0&&_find(n-1)!=_find(n))
136     {
137         printf("0\n");
138         return 0;
139     }
140 
141     int now=1,sum=0;
142     while(sum!=k)
143     {
144         for(int i=1;i<=m;i++)
145             add_edge(n*now+v[i][(now-1)%v[i].size()],n*(now+1)+v[i][now%v[i].size()],p[i]);
146         for(int i=1;i<=n;i++)
147             add_edge(n*now+i,n*(now+1)+i,inf);
148         add_edge(n*(now+1)+n,t,inf);
149         sum+=dinic(n*(now+2));
150         now++;
151     }
152     printf("%d\n",now-1);
153     return 0;
154 }

 

14、数字梯形问题

题目链接:https://www.oj.swust.edu.cn/problem/show/1751

建图方案,这个题就是套路拆点限制一下流量然后跑费用流就好了,具体的看看代码你们应该也能懂,把点拆成入点和出点

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cmath>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <ctime>
  7 #include <map>
  8 #include <queue>
  9 #include <cstdlib>
 10 #include <string>
 11 #include <climits>
 12 #include <set>
 13 #include <vector>
 14 using namespace std;
 15 const int MAXN=5001;
 16 const int MAXM=50001;
 17 bool vis[MAXN];
 18 int dist[MAXN];
 19 int s,t,ans;
 20 const int inf=1e9;
 21 int first[MAXN],sign;
 22 struct EDGE
 23 {
 24     int p,c,cc,next;
 25 } edge[MAXM];
 26  
 27 void creat()
 28 {
 29     memset(first,-1,sizeof(first));
 30     sign=ans=0;
 31 }
 32  
 33 void add_edge(int from,int to,int c,int cost)
 34 {
 35     edge[sign].p=to;
 36     edge[sign].c=c;
 37     edge[sign].cc=cost;
 38     edge[sign].next=first[from];
 39     first[from]=sign++;
 40     edge[sign].p=from;
 41     edge[sign].c=0;
 42     edge[sign].cc=-cost;
 43     edge[sign].next=first[to];
 44     first[to]=sign++;
 45 }
 46  
 47 inline bool spfa(int n,int s,int t)
 48 {
 49     memset(vis,0,sizeof vis);
 50     for(int i=0; i<=n; i++)dist[i]=1e9;
 51     dist[t]=0;
 52     vis[t]=1;
 53     deque<int>q;
 54     q.push_back(t);
 55     while(!q.empty())
 56     {
 57         int now=q.front();
 58         q.pop_front();
 59         for(int k=first[now]; k>-1; k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc)
 60             {
 61                 dist[edge[k].p]=dist[now]-edge[k].cc;
 62                 if(!vis[edge[k].p])
 63                 {
 64                     vis[edge[k].p]=1;
 65                     if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p);
 66                     else q.push_back(edge[k].p);
 67                 }
 68             }
 69         vis[now]=0;
 70     }
 71     return dist[s]<1e9;
 72 }
 73 inline int dfs(int x,int low)
 74 {
 75     if(x==t)
 76     {
 77         vis[t]=1;
 78         return low;
 79     }
 80     int used=0,a;
 81     vis[x]=1;
 82     for(int k=first[x]; k>-1; k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p])
 83         {
 84             a=dfs(edge[k].p,min(edge[k].c,low-used));
 85             if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a;
 86             if(used==low)break;
 87         }
 88     return used;
 89 }
 90 inline int costflow(int n)
 91 {
 92     int flow=0;
 93     while(spfa(n,s,t))
 94     {
 95         vis[t]=1;
 96         while(vis[t])
 97         {
 98             memset(vis,0,sizeof vis);
 99             flow+=dfs(s,1e9);
100         }
101     }
102     return flow;
103 }
104 int a[55][55];
105 int id[55][55],wid[55][55];
106 int main()
107 {
108     creat();
109     int n,m;
110     scanf("%d%d",&m,&n);
111     s=0;
112     int cnt=1;
113     for(int i=1; i<=n; i++)
114     {
115         for(int j=1; j<=m+i-1; j++)
116             scanf("%d",&a[i][j]),id[i][j]=cnt++,wid[i][j]=cnt++;
117     }
118     t=cnt;
119     for(int i=1; i<=n; i++)
120     {
121         for(int j=1; j<=m+i-1; j++)
122         {
123             add_edge(id[i][j],wid[i][j],1,-a[i][j]);
124             if(i!=n)
125             {
126                 add_edge(wid[i][j],id[i+1][j],1,0);
127                 add_edge(wid[i][j],id[i+1][j+1],1,0);
128             }
129         }
130     }
131     for(int i=1; i<=m; i++)
132         add_edge(s,id[1][i],1,0);
133     for(int i=1; i<=n+m-1; i++)
134         add_edge(wid[n][i],t,1,0);
135     costflow(cnt);
136     printf("%d\n",-ans);
137     creat();
138     for(int i=1; i<=n; i++)
139     {
140         for(int j=1; j<=m+i-1; j++)
141         {
142             add_edge(id[i][j],wid[i][j],inf,-a[i][j]);
143             if(i!=n)
144             {
145                 add_edge(wid[i][j],id[i+1][j],1,0);
146                 add_edge(wid[i][j],id[i+1][j+1],1,0);
147             }
148         }
149     }
150     for(int i=1; i<=m; i++)
151         add_edge(s,id[1][i],1,0);
152     for(int i=1; i<=n+m-1; i++)
153         add_edge(wid[n][i],t,inf,0);
154     costflow(cnt);
155     printf("%d\n",-ans);
156  
157     creat();
158     for(int i=1; i<=n; i++)
159     {
160         for(int j=1; j<=m+i-1; j++)
161         {
162             add_edge(id[i][j],wid[i][j],inf,-a[i][j]);
163             if(i!=n)
164             {
165                 add_edge(wid[i][j],id[i+1][j],inf,0);
166                 add_edge(wid[i][j],id[i+1][j+1],inf,0);
167             }
168         }
169     }
170     for(int i=1; i<=m; i++)
171         add_edge(s,id[1][i],1,0);
172     for(int i=1; i<=n+m-1; i++)
173         add_edge(wid[n][i],t,inf,0);
174     costflow(cnt);
175     printf("%d\n",-ans);
176     return 0;
177 }

 

21、最长k可重区间集问题

题目连接:https://www.oj.swust.edu.cn/problem/show/1756或者https://loj.ac/problem/6014

一年后我又开始了,,不过这次只想补几个经典题==

建图方案:把区间坐标离散化,然后每个点向下一个点连一条边,从起点往1连一条边,对于每个i,从l[i]->r[i]连一条边。这个建图是什么意思呢?就是说,你有k次选择机会,每次会选择一些不相交的区间,不选的话就直接不消耗费用走到下一个点,选的话就从l->r点。建图就是这样。

注意:poweroj上的数据有问题,会出现l[i]>r[i]的情况,但是你必须得当作不知道来建图2333,不然会wa8。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <queue>
  4 #include <cstring>
  5 #include <vector>
  6 using namespace std;
  7 const int MAXN=2111;
  8 const int MAXM=20001;
  9 bool vis[MAXN];int dist[MAXN];
 10 int s,t,ans;
 11 
 12 int first[MAXN],sign;
 13 struct EDGE
 14 {
 15     int p,c,cc,next;
 16 }edge[MAXM];
 17 
 18 void creat()
 19 {
 20     memset(first,-1,sizeof(first));
 21     sign=ans=0;
 22 }
 23 
 24 void add_edge(int from,int to,int c,int cost)
 25 {
 26     edge[sign].p=to;
 27     edge[sign].c=c;
 28     edge[sign].cc=cost;
 29     edge[sign].next=first[from];
 30     first[from]=sign++;
 31     edge[sign].p=from;
 32     edge[sign].c=0;
 33     edge[sign].cc=-cost;
 34     edge[sign].next=first[to];
 35     first[to]=sign++;
 36 }
 37 
 38 inline bool spfa(int n,int s,int t){
 39     memset(vis,0,sizeof vis);
 40     for(int i=0;i<=n;i++)dist[i]=1e9;dist[t]=0;vis[t]=1;
 41     deque<int>q;q.push_back(t);
 42     while(!q.empty()){
 43         int now=q.front();q.pop_front();
 44         for(int k=first[now];k>-1;k=edge[k].next)if(edge[k^1].c&&dist[edge[k].p]>dist[now]-edge[k].cc){
 45                 dist[edge[k].p]=dist[now]-edge[k].cc;
 46                 if(!vis[edge[k].p]){
 47                     vis[edge[k].p]=1;
 48                     if(!q.empty()&&dist[edge[k].p]<dist[q.front()])q.push_front(edge[k].p);else q.push_back(edge[k].p);
 49                 }
 50             }
 51         vis[now]=0;
 52     }
 53     return dist[s]<1e9;
 54 }
 55 inline int dfs(int x,int low){
 56     if(x==t){vis[t]=1;return low;}
 57     int used=0,a;vis[x]=1;
 58     for(int k=first[x];k>-1;k=edge[k].next)if(!vis[edge[k].p]&&edge[k].c&&dist[x]-edge[k].cc==dist[edge[k].p]){
 59             a=dfs(edge[k].p,min(edge[k].c,low-used));
 60             if(a)ans+=a*edge[k].cc,edge[k].c-=a,edge[k^1].c+=a,used+=a;
 61             if(used==low)break;
 62         }
 63     return used;
 64 }
 65 inline int costflow(int n){
 66     int flow=0;
 67     while(spfa(n,s,t)){
 68         vis[t]=1;
 69         while(vis[t]){
 70             memset(vis,0,sizeof vis);
 71             flow+=dfs(s,1e9);
 72         }
 73     }
 74     return flow;
 75 }
 76 vector<int>v;
 77 int getid(int x) {
 78     return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
 79 }
 80 int l[MAXN],r[MAXN];
 81 int main()
 82 {
 83     creat();
 84     int n,k;
 85     scanf("%d%d",&n,&k);
 86     for(int i=1;i<=n;i++){
 87         scanf("%d%d",l+i,r+i);
 88         if(l[i]>r[i])swap(l[i],r[i]);
 89         v.push_back(l[i]),v.push_back(r[i]);
 90     }
 91     sort(v.begin(),v.end());
 92     v.erase(unique(v.begin(),v.end()),v.end());
 93     int tot=v.size();
 94     s=0;t=tot+1;
 95     add_edge(s,1,k,0);
 96     for(int i=1;i<tot;i++)
 97         add_edge(i,i+1,k,0);
 98     add_edge(tot,t,k,0);
 99     for(int i=1;i<=n;i++) {
100         add_edge(getid(l[i]),getid(r[i]),1,l[i]-r[i]);
101     }
102     costflow(t);
103     printf("%d\n",-ans);
104     return 0;
105 }

 

推荐阅读