@lpf20051024
2020-08-11T14:51:04.000000Z
字数 9166
阅读 695
网络流
题解
都是从 cnblog 来的吧,代码就贴在这里啦。
配合 原讲解+少量注释 食用更佳。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#define N 110
#define M 100100
using namespace std;
int n,m,head[N],match[N],cnt=0,ans=0;
bool vis[N];
struct Edge{
int nxt,to;
}ed[M];
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void add(int u,int v){
ed[++cnt].nxt=head[u];
ed[cnt].to=v;
head[u]=cnt;
return;
}
bool dfs(int u){
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to;
if(vis[v]) continue;
vis[v]=true;
if(!match[v] || dfs(match[v])){
match[v]=u;return true;
}
}
return false;
}
int main(){
n=read(),m=read()-n;
int u=read(),v=read();
while(u!=-1 && v!=-1){
add(u,v),add(v,u);
u=read(),v=read();
}
for(int i=1;i<=n;i++){
memset(vis,false,sizeof(vis));
if(dfs(i)) ++ans;
}
printf("%d\n",ans);
for(int i=n+1;i<=n+m;i++)
if(match[i]) printf("%d %d\n",match[i],i);
return 0;
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#define N 1010
#define M 100010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,t,sum=0,head[N],d[N],cnt=1;
struct Edge{
int nxt,to,val;
}ed[M<<1];
queue<int>q;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void add(int u,int v,int w){
ed[++cnt].nxt=head[u];ed[cnt].to=v;ed[cnt].val=w;head[u]=cnt;
ed[++cnt].nxt=head[v];ed[cnt].to=u;ed[cnt].val=0;head[v]=cnt;
}
bool bfs(){
memset(d,0,sizeof(d));
while(!q.empty()) q.pop();
d[s]=1;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && !d[v]){
d[v]=d[u]+1;
if(v==t) return true;
q.push(v);
}
}
}
return false;
}
int dinic(int u,int flow){
if(u==t) return flow;
int rest=flow;
for(int i=head[u];i && rest;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && d[v]==d[u]+1){
int k=dinic(v,min(rest,w));
if(!k) d[v]=0;
ed[i].val-=k;
ed[i^1].val+=k;
rest-=k;
}
}
return flow-rest;
}
int main(){
n=read(),m=read();
s=0,t=n+m+1;
for(int i=1;i<=n;i++){
int a=read();sum+=a;//记录代表总数。
add(s,i,a);
}
for(int i=1;i<=m;i++){
int a=read();
add(i+n,t,a);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
add(i,j+n,1);
int ans=0,flow;
while(bfs())
if(flow=dinic(s,INF)) ans+=flow;
if(ans<sum){puts("0");return 0;}
puts("1");
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(v!=s && !w) printf("%d ",v-n);//根据残量网络输出。
}
puts("");
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#define N 510
#define M 100010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,t,head[N],fa[N],d[N],cnt=1;
struct Edge{
int nxt,frm,to,val;
}ed[M];
queue<int>q;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void add(int u,int v,int w){
ed[++cnt]=(Edge){head[u],u,v,w};head[u]=cnt;
ed[++cnt]=(Edge){head[v],v,u,0};head[v]=cnt;
}
void build(){//建图。
n=read(),m=read();
s=0,t=n<<1|1;
for(int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v+n,1);
}
for(int i=1;i<=n;i++) add(s,i,1);
for(int i=1;i<=n;i++) add(i+n,t,1);
return;
}
bool bfs(){
while(!q.empty()) q.pop();
memset(d,0,sizeof(d));
d[s]=1;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && !d[v]){
d[v]=d[u]+1;
if(v==t) return true;
q.push(v);
}
}
}
return false;
}
int Dinic(int u,int flow){
if(u==t) return flow;
int rest=flow;
for(int i=head[u];i && rest;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && d[v]==d[u]+1){
int k=Dinic(v,min(rest,w));
if(!k) d[v]=0;
ed[i].val-=k;
ed[i^1].val+=k;
rest-=k;
}
}
return flow-rest;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void Print(int u){
printf("%d ",u);
for(int i=head[u];i;i=ed[i].nxt)
if(!ed[i].val && ed[i].to>n)
Print(ed[i].to-n);//递归输出。
return;
}
void work(){
int flow,ans=0;
while(bfs())
if(flow=Dinic(s,INF)) ans+=flow;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=2;i<=cnt;i++){
int u=ed[i].frm,v=ed[i].to,w=ed[i].val;
if(u>s && u<=n && v>n && v<t && !w)//找到被匹配了的边,表示这两点被合并了。
fa[find(v-n)]=find(u);
//与一般并查集不同的是 v-n 和 u 的顺序不可调换。
//因为 u 在路径上的位置必然在 v-n 之前,而路径的输出需要考虑递归的顺序。
}
for(int i=1;i<=n;i++)
if(fa[i]==i) Print(i),puts("");
printf("%d\n",n-ans);
return;
}
int main(){
build();
work();
return 0;
}
//真就是一道挺板子的题目啊。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#define N 10010
#define M 200010
#define INF 0x3f3f3f3f
using namespace std;
int m,n,s,t,head[N],d[N],cnt=1;
struct Edge{
int nxt,to,val;
}ed[M];
queue<int>q;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void add(int u,int v,int w){
ed[++cnt]=(Edge){head[u],v,w};head[u]=cnt;
ed[++cnt]=(Edge){head[v],u,0};head[v]=cnt;
return;
}
bool bfs(){
while(!q.empty()) q.pop();
memset(d,0,sizeof(d));
d[s]=1;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && !d[v]){
d[v]=d[u]+1;
if(v==t) return true;
q.push(v);
}
}
}
return false;
}
int Dinic(int u,int flow){
if(u==t) return flow;
int rest=flow;
for(int i=head[u];i && rest;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && d[v]==d[u]+1){
int k=Dinic(v,min(rest,w));
if(!k) d[v]=0;
ed[i].val-=k;
ed[i^1].val+=k;
rest-=k;
}
}
return flow-rest;
}
int main(){
n=read(),m=read();
int a,b,sum=0;
s=0,t=n+m+1;
for(int i=1;i<=n;i++)
a=read(),sum+=a,add(s,i,a);
for(int i=1;i<=m;i++){
a=read();
while(a--) b=read(),add(b,i+n,1);
}
for(int i=1;i<=m;i++)
add(i+n,t,1);
int ans=0,flow;
while(bfs())
if(flow=Dinic(s,INF)) ans+=flow;
if(ans<sum){
puts("No Solution!");return 0;
}
for(int u=1;u<=n;u++){
printf("%d: ",u);
for(int i=head[u];i;i=ed[i].nxt)
if(!ed[i].val && ed[i].to>n) printf("%d ",ed[i].to-n);
puts("");
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#define N 1000010
#define M 2000010
#define s 0
#define t 1000000
#define INF 0x3f3f3f3f
using namespace std;
int n,head[N],frs[N],pre[N],d[N],cnt=1;
bool vis[N];
struct Edge{
int nxt,to,val;
}ed[M];
queue<int>q;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void add(int u,int v,int w){
ed[++cnt]=(Edge){head[u],v,w};head[u]=cnt;
ed[++cnt]=(Edge){head[v],u,0};head[v]=cnt;
return;
}
bool bfs(){
while(!q.empty()) q.pop();
memset(d,0,sizeof(d));
d[s]=1;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && !d[v]){
d[v]=d[u]+1;
if(v==t) return true;
q.push(v);
}
}
}
return false;
}
int Dinic(int u,int flow){
if(u==t) return flow;
int rest=flow;
for(int i=head[u];i && rest;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && d[v]==d[u]+1){
int k=Dinic(v,min(w,rest));
if(!k) d[v]=0;
ed[i].val-=k;
ed[i^1].val+=k;
rest-=k;
pre[u>>1]=v>>1;
//开始建图时乘 2 了。
//很巧的是 (u<<1)>>1=(u<<1|1)>>1=u。
}
}
return flow-rest;
}
int main(){
n=read();
int now=0,Pillar=0;
while(Pillar<=n){//如果柱子数 > n 就退出。
++now;
add(s,now<<1,1);//和源点连接。
add(now<<1|1,t,1);//和汇点连接。
for(int i=sqrt(now)+1;i*i<(now<<1);i++)//和比自己小的能与自己组成完全平方数的数连边。
add((i*i-now)<<1,now<<1|1,1);//注意细节。
int flow,ans=0;
while(bfs())
if(flow=Dinic(s,INF)) ans+=flow;
if(!ans)//如果找不到增广路。
frs[++Pillar]=now;
}
printf("%d\n",--now);//记得减 1,因为最后的球肯定是放在第 n+1 个柱子上的。
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++){
if(vis[frs[i]]) continue;//如果之前见过你了就不用输出了。
for(int u=frs[i];u && u!=(t>>1);u=pre[u])
vis[u]=true,printf("%d ",u);
puts("");
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define N 1000010
#define M 2000010
#define P 110
#define s 0
#define t N-10
#define INF 0x3f3f3f3f
using namespace std;
int n,m,k,ans,head[N],cnt=1;
int d[N];
int r[P],g[P][P],h[P];
int fa[P],q[N];
struct Edge{
int nxt,to,val;
}ed[M];
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void init(){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++){
h[i]=read(),r[i]=read();
for(int j=0;j<r[i];j++) g[i][j]=read();
}
return;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy) fa[fx]=fy;
return;
}
void pre_chck(){
for(int i=1;i<=n+2;i++) fa[i]=i;
for(int i=1;i<=m;i++)
for(int j=0;j<r[i];j++){
if(g[i][j]==0) g[i][j]=n+1;
if(g[i][j]==-1) g[i][j]=n+2;
if(j!=0) merge(g[i][j],g[i][j-1]);
}
if(find(n+1)!=find(n+2)){puts("0");exit(0);}
return;
}
void add(int u,int v,int w){
ed[++cnt]=(Edge){head[u],v,w};head[u]=cnt;
ed[++cnt]=(Edge){head[v],u,0};head[v]=cnt;
return;
}
bool bfs(){
for(int i=1;i<=ans*(n+1);i++) d[i]=0;//千万别用 memset。
d[t]=0;
d[s]=1;q[1]=s;
int h=1,tail=1;
while(h<=tail){
int u=q[h];++h;
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && !d[v]){
d[v]=d[u]+1;
if(v==t) return true;
q[++tail]=v;
}
}
}
return false;
}
int Dinic(int u,int flow){
if(u==t) return flow;
int i,rest=0;
for(i=head[u];i;i=ed[i].nxt){
int v=ed[i].to,w=ed[i].val;
if(w && d[v]==d[u]+1){
int k=Dinic(v,min(flow-rest,w));
if(!k) d[v]=0;
ed[i].val-=k;
ed[i^1].val+=k;
rest+=k;
if(rest==flow) return rest;
}
}
return rest;
}
int main(){
init();
pre_chck();//并查集判无解。
int now=0;
for(ans=1;;ans++){
add(s,ans*(n+1),INF);
for(int i=1;i<=m;i++){
int x=(ans-1)%r[i],y=ans%r[i];
if(g[i][x]==n+2) x=t;
else x=(ans-1)*(n+1)+g[i][x];
if(g[i][y]==n+2) y=t;
else y=ans*(n+1)+g[i][y];
add(x,y,h[i]);
}//日常建边。
while(bfs()) now+=Dinic(s,INF);//利用残量网络跑 Dinic。
if(now>=k){printf("%d\n",ans);return 0;}
for(int i=1;i<=n+1;i++)
add((ans-1)*(n+1)+i,ans*(n+1)+i,INF);//向下一层转移。
}
return 0;
}