【刷题记录】网络流24题等
令人崩溃的五道题
首先是网络流24题中的前五题
1.飞行员配对问题
标准的二分图匹配,这里采用时间复杂度最优秀O(sqrt(E)V)的网络流做法
建立源点s汇点t分别连接至二分图的两个部分
所有边权设置为1
跑最大流即可
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <algorithm> #include <queue> #include <cstring> using namespace std; #define N 105 #define next nico int head[N],to[N*N*2],next[N*N*2],dis[N*N*2],tot=1,d[N],s=0,t,n,m; void add(int x,int y, int z) { to[++tot]=y; dis[tot]=z; next[tot]=head[x]; head[x]=tot; } int bfs() { memset(d,0,sizeof(d)); queue <int> q; d[s]=1; q.push(s); while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; i; i = next[i]) { int des = to[i]; if(dis[i]&&!d[des]) { d[des]=d[x]+1; q.push(des); } } } return d[t]; } int dfs(int x,int v) { if(x==t||v==0)return v; int ans = 0; for(int i = head[x]; i ; i = next[i]) { int des = to[i]; if(d[des]==d[x]+1) { int f = dfs(des,min(dis[i],v)); v -= f; dis[i]-=f; dis[i^1]+=f; ans += f; } } return ans; } int main() { scanf("%d%d",&m,&n); int x=0,y=0; while(x!=-1) { scanf("%d%d",&x,&y); add(x,y,1); add(y,x,0); } for(int i = 1; i <= m; i ++) { add(n+2,i,1); add(i,n+2,0); } for(int i = m +1; i <= n ; i++) { add(i,n+1,1); add(n+1,i,0); } s=n+2;t=n+1; int ans = 0; while(bfs()) { ans +=dfs(s,0x7f7f7f7f); } if(ans!=0) { printf("%d\n",ans); for(int i = 1; i <= m; i ++) { for(int j = head[i];j;j=next[j]) { if(dis[j]==0&&to[j]<=n) { printf("%d %d\n",i,to[j]); } } } } else puts("No Solution!"); }
2.太空飞行计划问题
最大边权子图的模板题问题
将模型转化成二分图,对于正权点连接源点,负权点连接汇点
跑最大流即可
答案是正权点数总和减去最大流
选取的点是阻塞流到达的点
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <queue> using namespace std; #define N 1000 #define next nico int m, n; const int inf = 0x0f7f7f7f; int head[N], next[N * N], to[N], tot = 1, val[N]; int sum; void add(int x, int y, int z) { to[++tot] = y; next[tot] = head[x]; val[tot] = z; head[x] = tot; } int d[N]; int bfs(int s, int t) { memset(d, 0, sizeof(d)); d[s] = 1; queue<int> q; q.push(s); while (!q.empty()) { int x = q.front(); q.pop(); for (int i = head[x]; i; i = next[i]) if (val[i] && !d[to[i]]) { d[to[i]] = d[x] + 1; q.push(to[i]); } } return d[t]; } int dfs(int x, int v, int t) { if (x == t || v == 0) return v; int ans = 0; for (int i = head[x]; i; i = next[i]) if (d[x] + 1 == d[to[i]]) { int l = dfs(to[i], min(val[i], v), t); v-=l; ans += l; val[i] -= l; val[i ^ 1] += l; } return ans; } int main() { scanf("%d%d", &m, &n); char buf[10001]; cin.getline(buf, 10000); for (int i = 1; i <= m; i++) { memset(buf,0,sizeof(buf)); cin.getline(buf, 10000); int p = 0, a; while (sscanf(buf + p, "%d", &a) == 1) { if (p != 0) { add(i, a + m, inf); add(a + m, i, 0); } else { add(n + m + 1, i, a); add(i, n + m + 1, 0); sum+=a; } if (a == 0) p++; else while (a) { a /= 10; p++; } p++; } } for (int i = 1; i <= n; i++) { int a; scanf("%d", &a); add(m + i, n + m + 2, a); add(n + m + 2, m + i, 0); } int ans = 0; while (bfs(n + m + 1, n + m + 2)) { ans += dfs(n + m + 1, inf, n + m + 2); } for (int j = head[n + m + 1]; j; j = next[j]) if (d[to[j]] != 0) { printf("%d ", to[j]); } puts(""); for (int j = head[n + m + 2]; j; j = next[j]) if (d[to[j]]!= 0) { printf("%d ", to[j]-m); } puts(""); printf("%d\n", sum - ans); }
3.最小路径覆盖问题
转化成二分图匹配问题即可
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <queue> using namespace std; #define N 400 #define next nico int head[N],to[30005],next[30005],tot=1,dis[30005],n,m,d[N],s,t; bool vis[N]; void add(int x,int y,int z) { to[++tot]=y; next[tot]=head[x]; dis[tot]=z; head[x]=tot; } int bfs() { memset(d,0,sizeof(d)); queue<int> q; q.push(s); d[s]=1; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x];i;i=next[i]) if(dis[i]&&!d[to[i]]) { d[to[i]]=d[x]+1; q.push(to[i]); } } return d[t]; } int dfs(int x,int v) { if(x==t||v==0)return v; int ans = 0; for(int i = head[x];i;i = next[i]) if(d[to[i]]==d[x]+1) { int l = dfs(to[i],min(v,dis[i])); dis[i]-=l; dis[i^1]+=l; ans+=l; v-=l; } return ans; } void printans(int x) { vis[x]=1; printf("%d ",x); for(int i = head[x];i;i=next[i]) { if(!dis[i]&&!vis[to[i]-n]&&to[i]<=n*2) { printans(to[i]-n); } } } int main() { #ifdef TEST freopen("test.in","r",stdin); #endif scanf("%d%d",&n,&m); for(int i = 1 ; i <= m ; i ++) { int a,b,c=1; scanf("%d%d",&a,&b); add(a,b+n,c); add(b+n,a,0); } s = 2*n+1;t = s+1; for(int i = 1; i <= n ; i ++) { add(s,i,1); add(i,s,0); add(i+n,t,1); add(t,i+n,0); } int ans = 0; while(bfs()) { ans += dfs(s,0x7f7f7f7f); } for(int i = 1; i <= n; i ++) if(!vis[i]) { printans(i); putchar('\n'); } printf("%d",n-ans); }
4.魔术球问题
可以贪心做
我采取将其转化成最小路径覆盖问题解决
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #include <queue> #include <map> #include <cmath> using namespace std; #define next nico #define N 5000 #define M 2000 int head[N],d[N],next[N*N],to[N*N],val[N*N],tot=1; int ans[100][N],tot1; int vis[N]; void add(int a , int b ,int c) { to[++tot]=b; next[tot]=head[a]; val[tot]=c; head[a]=tot; } int s,t; void getans(int x) { ans[tot1][++ans[tot1][0]]=x-2; vis[x-2]=1; for(int i = head[x];i;i=next[i]) if(!val[i]&&to[i]>M&&!vis[to[i]-M]) { getans(to[i]-M+2); } } int bfs() { queue <int> q; q.push(s); memset(d,0,sizeof(d)); d[s]=1; while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; i; i = next[i]) if(val[i]&&!d[to[i]]) { d[to[i]]=d[x]+1; q.push(to[i]); } } return d[t]; } int dfs(int x,int v) { if(x==t||v==0)return v; int ans = 0; for(int i = head[x]; i; i = next[i]) if(d[to[i]]==d[x]+1) { int l = dfs(to[i],min(val[i],v)); v-=l; ans+=l; val[i]-=l; val[i^1]+=l; } return ans; } int main() { int n,i; scanf("%d",&n); s=1;t=2; int sum=0; for(i = 1; ; i ++) { add(s,i+2,1); add(i+2,s,0); add(t,i+M,0); add(i+M,t,1); for(int j = 1; j < i ; j ++) { int p = (int)sqrt((double)i+j); if(p*p==i+j) { add(j+2,i+M,1); add(i+M,j+2,0); } } while(bfs()) { sum+=dfs(s,0x7f7f7f7f); } if(i-sum>n)break; tot1 = 0; memset(vis,0,sizeof(vis)); for(int j = 1; j <= i; j ++) if(!vis[j]) { tot1++; ans[tot1][0]=0; getans(j+2); } } printf("%d\n",i-1); for(int i = 1 ; i<= tot1; i ++) { for(int j = 1; j <= ans[i][0];j ++) { printf("%d ",ans[i][j]); } puts(""); } }
5.圆桌问题
与普通的二分图匹配不同,连接源点边权赋值为容纳人数
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <queue> #include <vector> #include <cstring> using namespace std; #define N 1000 #define next nico int head[N],next[N*N],to[N*N],v[N*N],tot=1,s,t; int* val = v; int d[N]; void add(int x,int y,int z) { to[++tot]=y; v[tot]=z; next[tot]=head[x]; head[x]=tot; } int bfs() { memset(d,0,sizeof(d)); queue<int>q; q.push(s); d[s]=1; while(!q.empty()) { int x =q.front(); q.pop(); for(int i = head[x];i;i=next[i]) if(!d[to[i]]&&v[i]) { d[to[i]]=d[x]+1; q.push(to[i]); } } return d[t]; } int dfs(int x,int v) { if(x==t||v==0)return v; int ans = 0; for(int i = head[x]; i ; i = next[i]) if(d[to[i]]==d[x]+1) { int l = dfs(to[i],min(v,val[i])); val[i]-=l; val[i^1]+=l; v-=l; ans += l; } return ans ; } int main() { int m,n,sum=0; scanf("%d%d",&m,&n); s = m+n+1; t = s+1; for(int i = 1 ; i <= m; i ++) { int x; scanf("%d",&x); add(s,i,x); add(i,s,0); sum+=x; } for(int i = 1; i <= n ; i ++) { int x; scanf("%d",&x); add(i+m,t,x); add(t,i+m,0); } for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { add(j,i+m,1); add(i+m,j,0); } int ans = 0; while(bfs()) { ans += dfs(s,0x7f7f7f7f); } if(ans == sum) { puts("1"); for(int i = 1; i <= m; i ++) { for(int j = head[i];j;j=next[j]) { if(v[j]==0&&to[j]<=m+n) { printf("%d ",to[j]-m); } } puts(""); } } else { putchar('0'); } }