网1:飞行员匹配
题目链接:https://www.oj.swust.edu.cn/problem/show/1736
俩种做法:
/**********二分图匹配做法***********/ #include<bits/stdc++.h> using namespace std; const int M=102; int n,m; int e[M][M],match[M],used[M]; bool find(int u){ for(int i=1;i<=m;i++){ if(e[u][i]==1&&used[i]==0){ used[i]=1; if(match[i]==0||find(match[i])){ match[i]=u; return true; } } } return false; } int main(){ scanf("%d%d",&m,&n); int x,y; while(~scanf("%d%d",&x,&y)){ if(x==-1&&y==-1) break; e[x][y]=1; e[y][x]=1; } // if(m==n||m==0) int ans=0; for(int i=m+1;i<=n;i++){ memset(used,0,sizeof(used)); if(find(i)) ans++; } if(ans==0) return puts("No Solution!"),0; printf("%d ",ans); for(int i=1;i<=m;i++) if(match[i]) printf("%d %d ",i,match[i]); return 0; } /***************网络流**************/ #include<bits/stdc++.h> using namespace std; const int M=505; int cur[M],deep[M],head[M],s,t,tot; const int inf=0x3f3f3f3f; struct node{ int v,nextt,w; }e[M*50]; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ memset(deep,0,sizeof(deep)); queue<int>que; que.push(s); deep[s]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int x=0,ans=0; for(int i=cur[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(deep[u]+1==deep[v]&&e[i].w>0){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return fl; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=t;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } int main(){ memset(head,-1,sizeof(head)); int n,m; cin>>m>>n; s=0,t=n+1; for(int i=1;i<=m;i++){ addedge(s,i,1); } for(int i=m+1;i<=n;i++){ addedge(i,t,1); } int x,y; while(~scanf("%d%d",&x,&y)){ if(x==-1&&y==-1) break; addedge(x,y,1); } int ans=dinic(t); if(ans==0) return puts("No Solution!"),0; printf("%d ",ans); for(int i=m+1;i<=n;i++) for(int j=head[i];j!=-1;j=e[j].nextt) if(e[j].v!=t&&e[j].w==1) printf("%d %d ",e[j].v,i); return 0; }
网2:飞行员计划问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1737
最大权闭合图(总的答案就是最小路径覆盖的答案)
从起点往每个权值为正的点建立一条边,容量为点权值。
每个权值为负的点往终点建立一条边,容量为权值的绝对值。
如果选A就必须选B 则就从A建立一条往B的边,容量为inf。
最大权闭合图的点就是从起点开始广搜,权值为0的点不走,能走到的点就是被选中的点。
dinic最后一次bfs的d数组正好可以用来判断这个条件。
#include<bits/stdc++.h> using namespace std; const int M=505; const int inf=0x3f3f3f3f; struct node{ int v,w,nextt; }e[M*50]; int head[M],cur[M],deep[M],s,t,tot; bool bfs(){ memset(deep,0,sizeof(deep)); queue<int >que; que.push(s); deep[s]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==t) return true;; que.push(v); } } } return false; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,x=0; for(int i=cur[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==deep[u]+1){ x=dfs(v,min(e[i].w,fl-ans)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return fl; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } void init(){ memset(head,-1,sizeof(head)); tot=0; } int main(){ init(); int m,n; cin>>m>>n; s=0,t=n+m+1; int sum=0; for(int i=1;i<=m;i++){ int w; scanf("%d",&w); addedge(s,n+i,w); sum+=w; int x; char ch; while(true){ scanf("%d%c",&x,&ch); addedge(n+i,x,inf); if(ch==' '||ch==' ') break; } } for(int i=1;i<=n;i++){ int x; scanf("%d",&x); addedge(i,t,x); } sum-=dinic(t); for(int i=1;i<=m;i++){ if(deep[n+i]) printf("%d ",i); } putchar(' '); for(int i=1;i<=n;i++){ if(deep[i]) printf("%d ",i); } putchar(' '); printf("%d ",sum); return 0; }
3、最小路径覆盖问题
题目链接:https://loj.ac/problem/6002
最小路径覆盖问题模型,拆点做最大匹配,总点数减去最大流为需要的最少的边数
题目有建图方法
拆点意思是这样的,第i个点的流量流向第j个点时表示第i个点的下一个点是j点,剩下的没办法流向终点的点就是没有点能到它那里,说明这个点是边的起点,根据这个来递归可以用来查找答案方案。
#include<bits/stdc++.h> using namespace std; const int M=505; const int inf=0x3f3f3f3f; struct node{ int v,nextt,w; }e[M*50]; int head[M],book[M],cur[M],deep[M],s,t,tot,n; inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') x=0; ch=getchar(); } while(ch>='0'&&ch<='9') sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); return x?sum:-sum; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ memset(deep,0,sizeof(deep)); queue<int>que; que.push(s); deep[s]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,x=0; for(int i=cur[u];i!=-1;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==deep[u]+1){ x=dfs(v,min(e[i].w,fl-ans)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return ans; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } void dfs2(int u,int &flag){ int point=u+n; book[u]=1; for(int i=head[point];i!=-1;i=e[i].nextt){ if(e[i].v!=t&&e[i].w==1){ dfs2(e[i].v,flag); } } if(flag==1) flag=0; else putchar(' '); write(u); } void init(){ tot=0; memset(head,-1,sizeof(head)); } int main(){ init(); n=read(); int m; m=read(); s=0,t=(n<<1)+1; for(int i=1;i<=n;i++) addedge(s,i,1),addedge(i+n,t,1); while(m--){ int x=read(),y=read(); addedge(x,y+n,1); } int sum=n-dinic(t); for(int i=head[t];i!=-1;i=e[i].nextt){ int flag=1; if(!book[e[i].v-n]&&e[i].w==1){ dfs2(e[i].v-n,flag); puts(""); } } for(int i=1;i<=n;i++) if(!book[i]) write(i),putchar(' '); write(sum); putchar(' '); return 0; }
4、魔术球问题
题目链接:https://www.oj.swust.edu.cn/problem/show/1739
网络流跑形成的每条链就是题目要求的管子
由此答案就是各个点形成网络流的最小路径覆盖
因为网络流是在残量网络流上跑的,所以我们每次加一个点进去,在把符合条件的边加上去
注意判断条件:for(i=1; i-ans<=n+1; i++)
当加到刚好超过给点的n时即为答案,I是网络流上的点(除起点,汇点外),ans是最大流;
然后输出方案那里/2的意思因为我每次建点都是健俩点,所以输出方案点数编号的时候对应的就是其1/2倍。
#include<bits/stdc++.h> using namespace std; const int M=11111; const int inf=0x3f3f3f3f; struct node { int v,nextt,w; }e[M*20]; int head[M],cur[M],deep[M],hei[M],y[M],book[M],s,t,tot,cnt; inline int read(){ int sum=0,sign=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') sign=0; ch=getchar(); } while(ch>='0'&&ch<='9') sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); return sign?sum:-sum; } inline void write(int f){ if(f<0) putchar('-'),f=-f; if(f>9) write(f/10); putchar(f%10+'0'); } bool bfs(){ memset(deep,0,sizeof(deep)); queue<int>que; que.push(s); deep[s]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,p=0; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==deep[u]+1&&e[i].w>0){ p=dfs(v,min(fl-ans,e[i].w)); e[i].w-=p; e[i^1].w+=p; if(e[i].w) cur[u]=i; ans+=p; if(ans==fl) return fl; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()) { for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } void init(){ memset(head,-1,sizeof(head)); tot=0,cnt=2; } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } void prin(int u2,int &flag){ book[u2]=1; int u=y[u2]; for(int i=head[u];~i;i=e[i].nextt){ if(e[i].w==1&&e[i].v!=t) prin(e[i].v/2,flag); } if(flag) flag=0; else putchar(' '); write(u2); } int main(){ init(); int n=read(); s=0,t=11110; int i,ans=0; for(i=1;i-ans<=n+1;i++){ hei[i]=cnt++; y[i]=cnt++; addedge(s,hei[i],1); addedge(y[i],t,1); for(int j=1;j<i;j++){ int l=sqrt(i+j); if(l*l==i+j){ addedge(hei[j],y[i],1); } } ans+=dinic(t); } int sum=i-2; write(sum); putchar(' '); int flag=1; for(int i=head[t];~i;i=e[i].nextt){ if(e[i].w==1&&!book[e[i].v/2]){ flag=1; prin(e[i].v/2,flag); putchar(' '); } } for(int i=1;i<=sum;i++) if(!book[i]) write(i),putchar(' '); return 0; }
5、圆桌问题
题目链接:https://www.luogu.org/problemnew/show/P3254
建图问题:健超级源点s,超级汇点t
s向每个代表连一条边,容量为人数------1
每个代表向每个圆桌拆个点出来连,容量为1(代表要一个人)-----2
最后每个圆桌向超级汇点连边,容量为圆桌允许人数ci-----3
第一步的编号为:1~n;
第二步的编号为:n+1~n+n*m;
第三步的编号为:n+n*m+1~n+n*m+m;
所以t编号为n+n*m+m+1;
跑网络流看最大流等不等与代表总人数
然后就是输出方案:
因为有走过,所以邻接边为1(看代码应该看得懂)
输出答案的话就找那个限制流量的边,如果那个边走过了就说明这个点有用嘛,找一下输出就行。
#include<bits/stdc++.h> using namespace std; const int M=50005; const int inf=0x3f3f3f3f; struct node{ int v,nextt,w; }e[M*20]; int head[M],cur[M],deep[M],tot,s,t; inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') x=0; ch=getchar(); } while(ch>='0'&&ch<='9') sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); return x?sum:-sum; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ memset(deep,0,sizeof(deep)); deep[s]=1; queue<int>que; que.push(s); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==0&&e[i].w>0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,x=0; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==deep[u]+1){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return ans; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } void init(){ tot=0; memset(head,-1,sizeof(head)); } vector<int>a[M]; int main(){ init(); int n=read(),m=read(); s=0,t=n+n*m+m+1; int sum=0; for(int i=1;i<=n;i++){ int x=read(); sum+=x; addedge(s,i,x); } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ addedge(i,n+(i-1)*m+j,1); addedge(n+(i-1)*m+j,n+n*m+j,1); } } for(int i=1;i<=m;i++){ int x=read(); addedge(n+n*m+i,t,x); } if(dinic(t)==sum){ puts("1"); } else return puts("0"),0; //cout<<"!!"<<endl; for(int i=1;i<=m;i++){ for(int j=head[n+n*m+i];~j;j=e[j].nextt){ if(e[j].v!=t&&e[j].w==1){ a[(e[j].v-n-i)/m+1].push_back(i); } } } for(int i=1;i<=n;i++){ write(a[i][0]); for(int j=1;j<a[i].size();j++) putchar(' '),write(a[i][j]); putchar(' '); } return 0; }
6、最长递增子序列问题
题目链接:https://www.luogu.org/problemnew/show/P2766
第一问就用nlogn的dp去做,然后第二问第三问就用dp过程来模拟走网络流过程,建图也是如此;
将每个点拆点边权为1(代表只能用一次),然后其他看代码建图应该能懂;
第三问特殊地要求x1和xn可以重复使用,只需取消这两个点相关边的流量限制,求网络最大流即可。
#include<bits/stdc++.h> using namespace std; const int M=805; const int inf=0x3f3f3f3f; struct node{ int v,nextt,w; }e[20005]; int head[20005],cur[20005],deep[20005],s,t,tot,a[M],dp[M]; void init(){ tot=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ memset(deep,0,sizeof(deep)); queue<int>que; que.push(s); deep[s]=1; while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==0&&e[i].w>0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int x=0,ans=0; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==deep[u]+1&&e[i].w>0){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return ans; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } int main(){ int n; scanf("%d",&n); s=0,t=n*2+1; init(); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); dp[i]=1; } for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[i]>=a[j]) dp[i]=max(dp[j]+1,dp[i]); int ans=0; for(int i=1;i<=n;i++){ ans=max(ans,dp[i]); } for(int i=1;i<=n;i++) if(dp[i]==ans) addedge(s,i,1); for(int i=1;i<=n;i++) addedge(i+n,i,1); for(int i=1;i<=n;i++) if(dp[i]==1) addedge(i,t,1); for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[i]>=a[j]&&dp[i]==dp[j]+1) addedge(i,j+n,1); int c=dinic(t); printf("%d %d ",ans,c); if(dp[n]==ans) addedge(s,n,inf); addedge(1+n,1,inf); addedge(1,t,inf); c+=dinic(t); printf("%d ",c); return 0; }
7、试题库问题
题目链接:https://www.luogu.org/problemnew/show/P2763
////把k个项目向汇点t连容量为ki的容量;
把n个点拆点出来,容量为1(代表用一次)
#include<bits/stdc++.h> using namespace std; const int M=5e+5; const int inf=0x3f3f3f3f; struct node{ int v,w,nextt; }e[M<<1]; int head[M],cur[M],deep[M],s,t,tot; bool bfs(){ memset(deep,0,sizeof(deep)); queue<int>que; deep[s]=1; que.push(s); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==0&&e[i].w>0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,x=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(deep[v]==deep[u]+1&&e[i].w>0){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return ans; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } void init(){ memset(head,-1,sizeof(head)); tot=0; } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } int main(){ init(); int k,n; scanf("%d%d",&k,&n); s=0,t=2*n+k+1; int sum=0; for(int i=1;i<=k;i++){ int x; scanf("%d",&x); addedge(i,t,x); sum+=x; } for(int i=1;i<=n;i++) addedge(s,k+i,1); for(int i=1;i<=n;i++){ int p; addedge(k+i,k+i+n,1); scanf("%d",&p); while(p--){ int sign; scanf("%d",&sign); addedge(k+i+n,sign,1); } } if(dinic(t)!=sum) return puts("No Solution!"),0; for(int i=1;i<=k;i++){ printf("%d:",i); for(int j=head[i];~j;j=e[j].nextt){ if(e[j].v!=t&&e[j].w==1){ printf(" %d",e[j].v-k-n); } } putchar(' '); } return 0; }
8、机器人路径规划问题
不会题;
9、方格取数问题
题目链接:https://www.luogu.org/problemnew/show/P2774
方格取数
题目说不能取有边界
那么我们建图的时候就按有边界的取建图;
我们可以这样理解,我们要去掉一些格子,让整个图按要求联通,达到最大;
把每个方格拆点,边权为方格的值,最大对应全部-最小,最小什么呢?即最小割;
#include<bits/stdc++.h> using namespace std; inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') x=0; ch=getchar(); } while(ch>='0'&&ch<='9'){ sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); } return x?sum:-sum; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } const int M=2e4+4; const int inf=0x3f3f3f3f; struct node{ int v,nextt,w; }e[200005]; int head[M],deep[M],cur[M]; int tot,s,t; int gx[4]={1,-1,0,0}; int gy[4]={0,0,-1,1}; int a[102][102]; void init(){ memset(head,-1,sizeof(head)); tot=0; } void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ memset(deep,0,sizeof(deep)); deep[s]=1; queue<int>que; que.push(s); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==0){ deep[v]=deep[u]+1; if(v==t) return true; que.push(v); } } } return deep[t]==0?false:true; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0,x=0; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w>0&&deep[v]==deep[u]+1){ x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; if(e[i].w) cur[u]=i; ans+=x; if(ans==fl) return ans; } } if(ans==0) deep[u]=0; return ans; } int dinic(int n){ int ans=0; while(bfs()){ //cout<<"!!"<<endl; for(int i=0;i<=n;i++) cur[i]=head[i]; ans+=dfs(s,inf); } return ans; } int main(){ init(); int n=read(),m=read(); //cout<<n<<"~~~~~"<<m<<endl; s=0,t=n*m+1; int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(),ans+=a[i][j]; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if((i+j)&1) addedge((i-1)*m+j,t,a[i][j]); else addedge(s,(i-1)*m+j,a[i][j]); } } int sign=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(!((i+j)&1)){ //cout<<"#####"<<sign++<<"########"<<endl; for(int k=0;k<4;k++){ int tx=i+gx[k]; int ty=j+gy[k]; if(tx>=1&&tx<=n&&ty>=1&&ty<=m){ addedge((i-1)*m+j,(i-1)*m+j+(tx-i)*m+(ty-j),inf); //cout<<(i-1)*m+j+(tx-i)*m+(ty-j)<<endl; } } } } } write(ans-dinic(t)); return 0; }
18、最长k可重区间集问题
题:https://www.luogu.org/problem/P3358
做法:把数轴上的每一个点向它右边的点连一条边,容量为kk,费用为00,然后把每一个区间的左端点向右端点连边,容量为11,费用为区间长度,然后求一个最大费用最大流。因为坐标太大,记得离散
然而并不是很明白为什么这样做是对的……想了想,把网络流当成一个水流好了,水从左流到右,那么如果是在一个区间内,不可能满流(因为被区间左端点至右端点那一条边给分去了一部分流),但是被分去的那一部分流会在区间右端点被流回来,所以不想交的区间是没有影响的(因为是开区间,所以右端点和另一区间左端点重合并没有影响)。然后如果区间内还有其他区间的左端点,又会分流,一直这样下去,直到有超过kk个区间覆盖了同一点,那样流就不够了,不会再分(因为从源点也只有kk的流),那么只要求出了一个最大流,就是一个满足题目条件的方案。又因为要使长度最大,那么我们要让区间的流带上费用,求一个最大费用流即可
#include<bits/stdc++.h> using namespace std; inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') x=0; ch=getchar(); } while(ch>='0'&&ch<='9') sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); return x?sum:-sum; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } const int M=1e4+4; const int N=2e3+3; const int inf=0x3f3f3f3f; typedef long long ll; struct node{ ll cost; int v,w,nextt; }e[M]; int tot,s,t; ll mincost; int head[N],cur[N],vis[N],a[N<<1],l[N],r[N]; ll dis[N]; void addedge(int u,int v,int w,int cost){ e[tot].v=v; e[tot].w=w; e[tot].cost=cost; e[tot].nextt=head[u]; head[u]=tot++; e[tot].v=u; e[tot].w=0; e[tot].cost=-cost; e[tot].nextt=head[v]; head[v]=tot++; } bool bfs(){ for(int i=0;i<=t;i++) dis[i]=inf; queue<int>que; que.push(s); dis[s]=0; while(!que.empty()){ int u=que.front(); que.pop(); vis[u]=0; for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(e[i].w&&dis[v]>dis[u]+e[i].cost){ dis[v]=dis[u]+e[i].cost; if(!vis[v]){ vis[v]=1; que.push(v); } } } } return dis[t]!=inf; } int dfs(int u,int fl){ if(u==t) return fl; int ans=0; vis[u]=1; for(int i=cur[u];~i;i=e[i].nextt){ int v=e[i].v; if(!vis[v]&&e[i].w&&dis[v]==dis[u]+e[i].cost){ cur[u]=i; int x=dfs(v,min(fl-ans,e[i].w)); e[i].w-=x; e[i^1].w+=x; ans+=x; mincost+=1ll*x*e[i].cost; if(ans==fl) break; } } vis[u]=0; return ans; } void MFMC(){ while(bfs()){ for(int i=0;i<=t;i++) cur[i]=head[i]; dfs(s,inf); } } int main(){ memset(head,-1,sizeof(head)); int n=read(),k=read(); for(int i=1;i<=n;i++){ l[i]=read(),r[i]=read(); if(l[i]>r[i]) swap(l[i],r[i]); a[i]=l[i],a[i+n]=r[i]; } sort(a+1,a+1+n+n); int m=unique(a+1,a+1+n+n)-(a+1); for(int i=1;i<=n;i++){ int L=lower_bound(a+1,a+1+m,l[i])-a; int R=lower_bound(a+1,a+1+m,r[i])-a; addedge(L,R,1,l[i]-r[i]); } for(int i=2;i<=m;i++) addedge(i-1,i,k,0); s=0,t=m+1; addedge(s,1,k,0); addedge(m,t,k,0); MFMC(); printf("%lld ",-mincost); return 0; }