//没事偷偷编辑一下
网络流24题 (这之间有什么顺序吗)
由水到难。
传送门
两种方法啦啦啦啦
第一种就是显然而又直接的二分图匹配
#include
#include
#include
#include
#include
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MID int mid=(l+r)>>1
using namespace std;
struct mat{int to,next;}E[501];
int n,L,R,tot,head[250],Ans;
int nowlink[501],used[501];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar();
if(ch=='-')res=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return x*res;
}
void link(int u,int v){E[++tot]=(mat){v,head[u]};head[u]=tot;}
bool match(int k)
{
for(int e=head[k];e;e=E[e].next)
{
if(!used[E[e].to])
{
used[E[e].to]=1;
if(!nowlink[E[e].to] || match(nowlink[E[e].to]))
{
nowlink[E[e].to]=1;
return 1;
}
}
}
return 0;
}
int main()
{
n=gi();L=gi();R=n-L;int u,v;tot=1;
while(~scanf("%d%d",&u,&v))
{
if(u>v)swap(u,v);
link(u,v);link(v,u);
}
for(int i=1;i<=L;++i)
{
memset(used,0,sizeof(used));
if(match(i))++Ans;
}
printf("%d\n",Ans);
return 0;
}
第二种就是最大流了 左加S右加T 折腾自己
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
#define INF 0x7fffff
using namespace std;
struct Dinic{int to,next,C;}E[501];
int n,m,head[501],tot,dep[501];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar();
if(ch=='-')res=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int w)
{
++tot;
E[tot]=(Dinic){v,head[u],w};head[u]=tot;
}
void BFS(int s,int t)
{
memset(dep,0,sizeof(dep));dep[s]=1;
queue Q;Q.push(s);
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head[now];e;e=E[e].next)
{
if(dep[E[e].to])continue;
if(E[e].C<=0)continue;
int x=E[e].to;
dep[x]=dep[now]+1;
if(!(t^x))return;
Q.push(x);
}
}
return;
}
int aug(int now,int t,int val)
{
if(!(now^t))return val;
int re=0,c;
for(int e=head[now];e;e=E[e].next)
{
int x=E[e].to;
if(dep[x]==dep[now]+1 && E[e].C>0)
{
c=aug(x,t,min(val-re,E[e].C));
E[e].C-=c;
E[e^1].C+=c;
re+=c;
if(!(re^val))return val;
}
}
return re;
}
int search(int s,int t)
{
int F=0;
while(1)
{
BFS(s,t);if(!dep[t])return F;
F+=aug(s,t,INF);
}
return F;
}
int main()
{
n=gi();m=gi();int u,v;tot=1;
while(~scanf("%d%d",&u,&v))
{
if(u>v)swap(u,v);
ADD(u,v,1);ADD(v,u,0);
}
int s=n+1,t=n+2;
for(int i=1;i<=m;++i)
ADD(s,i,1),ADD(i,s,0);
for(int i=m+1;i<=n;++i)
ADD(i,t,1),ADD(t,i,0);
printf("%d\n",search(s,t));
}
这题不难 基本上就是来练手的
传送门
啊有三问。
第一问...直接DP...因为数据很小又不想打二分打个n^2找ans1没毛病的。
第二问...直接最大流吧...建图防止就是DP[i]连到DP[i+1]的点,S连到DP[1]的点,DP[ans1]的点连到T,每个点只能用一次所以流量是1。剩下的就是Dinic求ans2了。
第三问...别的没什么...1和n谨慎一点。1的DP值肯定是1,所以S连1一条INF的边。还有就是如果DP[n]==ans1才连n-T边。随便水水就可以找ans3了。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
struct Node{int to,C,next;}E[10100];
int n,m,dep[1010],tot=1,head[1010],a[1010],f[1010];
int S,T;
using namespace std;
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int c)
{
++tot;
E[tot]=(Node){v,c,head[u]};
head[u]=tot;
}
void link(int u,int v,int c)
{
ADD(u,v,c);
ADD(v,u,c);
}
void BFS(int s,int t)
{
if(s==t)return;
memset(dep,0,sizeof(dep));
dep[s]=1;
queue Q;
Q.push(s);
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head[now];e;e=E[e].next)
{
int x=E[e].to;
if(E[e].C<=0 || dep[x])continue;
dep[x]=dep[now]+1;
if(x==t)break;
Q.push(x);
}
}
return;
}
int DFS(int s,int t,int val)
{
if(s==t)return val;
int f=0,c;
for(int e=head[s];e;e=E[e].next)
{
int now=E[e].to;
if(E[e].C<=0 || dep[now]!=dep[s]+1)continue;
c=DFS(now,t,min(E[e].C,val-f));
E[e].C-=c;
E[e^1].C+=c;
f+=c;
if(f==val)return val;
}
return f;
}
int search(int s,int t)
{
int F=0;
while(1)
{
BFS(s,t);
if(!dep[t])break;
F+=DFS(s,t,1000000);
}
return F;
}
int main()
{
n=gi();S=n+1;T=n+2;
for(int i=1;i<=n;++i)
a[i]=gi(),f[i]=1;
for(int i=2;i<=n;++i)
for(int j=1;j=a[j])
f[i]=max(f[i],f[j]+1);
for(int i=1;i<=n;++i)
m=max(m,f[i]);
printf("%d\n",m);
for(int i=2;i<=n;++i)
for(int j=1;j=a[j] && f[i]==f[j]+1)
link(j,i,1);
for(int i=1;i<=n;++i)
if(f[i]==1)link(S,i,1);
for(int i=1;i<=n;++i)
if(f[i]==m)link(i,T,1);
printf("%d\n",search(S,T));
if(m==1){printf("%d",n);return 0;}
tot=1;memset(head,0,sizeof(head));
for(int i=1;i<=n;++i)
for(int j=1;j=a[j] && f[i]==f[j]+1)
link(j,i,1);
link(S,1,10000);
for(int i=2;i<=n;++i)
if(f[i]==1)link(S,i,1);
for(int i=1;i
传送门
最小(大)费用最大流的板子好伐,不解释了。
没看懂那个供需平衡有什么用,不平衡又能怎样呢。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MID int mid=(l+r)>>1
using namespace std;
const int NN=20100;
struct Node{int to,C,val,next;};
Node E1[NN],E2[NN];
int n,m,far[NN],head1[NN],head2[NN],tot1,tot2,S,T,up[NN],inQ[NN];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD1(int u,int v,int c,int w)
{
++tot1;
E1[tot1]=(Node){v,c,w,head1[u]};
head1[u]=tot1;
}
void ADD2(int u,int v,int c,int w)
{
++tot2;
E2[tot2]=(Node){v,c,w,head2[u]};
head2[u]=tot2;
}
void link(int u,int v,int c,int w)
{
ADD1(u,v,c,w);
ADD1(v,u,0,-w);
ADD2(u,v,c,-w);
ADD2(v,u,0,w);
}
void BFS1(int s,int t)
{
memset(far,127/3,sizeof(far));
memset(up,0,sizeof(up));
memset(inQ,0,sizeof(inQ));
queueQ;
Q.push(s);
far[s]=0;
inQ[s]=1;
while(!Q.empty())
{
int now=Q.front();Q.pop();inQ[now]=0;
for(int e=head1[now];e;e=E1[e].next)
{
if(E1[e].C<=0)continue;
int x=E1[e].to;
if(far[now]+E1[e].valQ;
Q.push(s);
far[s]=0;
inQ[s]=1;
while(!Q.empty())
{
int now=Q.front();Q.pop();inQ[now]=0;
for(int e=head2[now];e;e=E2[e].next)
{
if(E2[e].C<=0)continue;
int x=E2[e].to;
if(far[now]+E2[e].val
看到这题呢,不禁吐槽。好好的一道贪心题怎么变成网络流就变了味儿呢。
传送门
毁题的网络流啊...
好说正事。既然可以邻座相传那就连边吧,S到i(1<=i<=n)连库存不要钱,i到T连平均数不要钱,邻居连INF一块钱。(反正数据被恶意改小)
鬼出最小费用最大流。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
using namespace std;
struct Node{LL to,C,val,next;}E[810];
LL n,a[210],head[210],tot,ave,S,T;
LL far[210],up[210];
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(LL u,LL v,LL c,LL w)
{
++tot;
E[tot]=(Node){v,c,w,head[u]};
head[u]=tot;
}
void link(LL u,LL v,LL c,LL w)
{
ADD(u,v,c,w);
ADD(v,u,0,-w);
}
void SPFA()
{
for(LL i=1;i<=2*n+2;++i)
far[i]=2147483647/3,up[i]=0;
far[S]=0;
queue Q;
Q.push(S);
while(!Q.empty())
{
LL now=Q.front();Q.pop();
for(LL e=head[now];e;e=E[e].next)
{
if(E[e].C<=0)continue;
LL x=E[e].to;
if(far[now]+E[e].val
传送门
所以说这道题既可以用二分图最佳匹配又可以用网络流咯
n*n的连边嘛,S连左边人,流量1 不要钱。右边工作连T 流量1 不给钱 贪婪老板与狡诈员工。跑两遍,切掉。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MID int mid=(l+r)>>1
using namespace std;
const int NN=10100;
struct Node{int to,C,val,next;};
Node E1[NN],E2[NN];
int n,far[NN],head1[NN],head2[NN],tot1,tot2,S,T,up[NN];
int gi()
{
int x=0;char ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x;
}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD1(int u,int v,int c,int w)
{
++tot1;
E1[tot1]=(Node){v,c,w,head1[u]};
head1[u]=tot1;
}
void ADD2(int u,int v,int c,int w)
{
++tot2;
E2[tot2]=(Node){v,c,w,head2[u]};
head2[u]=tot2;
}
void link(int u,int v,int c,int w)
{
ADD1(u,v,c,w);
ADD1(v,u,0,-1*w);
ADD2(u,v,c,-1*w);
ADD2(v,u,0,w);
}
void BFS1(int s,int t)
{
memset(far,127/3,sizeof(far));
memset(up,0,sizeof(up));
queueQ;
Q.push(s);
far[s]=0;
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head1[now];e;e=E1[e].next)
{
if(E1[e].C<=0)continue;
int x=E1[e].to;
if(far[now]+E1[e].valQ;
Q.push(s);
far[s]=0;
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head2[now];e;e=E2[e].next)
{
if(E2[e].C<=0)continue;
int x=E2[e].to;
if(far[now]+E2[e].val
//2017年02月03日18:10:24止
传送门
这道题的图其实很好建... 跟着逻辑走就可以了,拆不拆点是无所谓的。一天只需要买R[i]个餐巾,就从S到i连流量R[i]代价p的边;然后往快洗慢洗天数后面连相应的边,再每个点向T连边就可以了。具体的就看看代码吧(我不是懒只是颓)。
#include
#include
#include
#include
#include
#include
#include
#define INF 1000000
#define link QT
#define LL long long int
#define MID int mid=(l+r)>>1
using namespace std;
struct Node{int to,C,w,next;}E[10100];
int R[201],tot=1,head[501],n,cost,lowd,lowc,fasd,fasc,S,T,far[601],up[601];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int c,int w)
{
++tot;
E[tot]=(Node){v,c,w,head[u]};
head[u]=tot;
}
void link(int u,int v,int c,int val)
{
ADD(u,v,c,val);
ADD(v,u,0,-val);
}
void build()
{
for(int i=1;i<=n;++i)
link(S,i,R[i],0),link(n+i,T,R[i],0);
for(int i=1;i<=n;++i)
{
if(i+1<=n)link(i,i+1,INF,0);
if(i+fasd<=n)link(i,i+n+fasd,INF,fasc);
if(i+lowd<=n)link(i,i+n+lowd,INF,lowc);
link(S,n+i,INF,cost);
}
}
void SPFA(int s,int t)
{
for(int i=1;i<=T;++i)far[i]=INF;
for(int i=1;i<=T;++i)up[i]=0;
far[s]=0;
queue Q;
Q.push(s);
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head[now];e;e=E[e].next)
{
if(E[e].C<=0)continue;
int x=E[e].to,X=E[e].w;
if(far[now]+X
//系统队列真的好慢啊
传送门
真的贼有意思的题目,连边连出了成就感。
又是三问...
第一问 限制每个点只能流入流出一次,那就裂点,中间连一条流量只有1的边就好了,再跑最大费用最大流。
第二问 每个节点的流入流出不是只有一次,但必须相等,于是可以想到每条边流量只有1就好了。值得一提的是最后一层的点流向T的边流量可以大,因为边在最后一层的点处重合是合法的而且坑你的数据不少。这是我第一遍做没想到的。
第三问 要求这么友善就很好办了。S到第一层连C=1,点向下一层连大数据,随随便便就可以鬼掉这道题。
看不懂的自己体会一下就懂了(我不是懒 #.#)
代码糊上。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
#define MID int mid=(l+r)>>1
const int N = 650;
using namespace std;
struct Node{int to,C,val,next;}E[N*N];
int n,m,head[3*N],tot,far[3*N],up[3*N],S,T;
int num[22][44],cnt,w[22][44];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int c,int w)
{
++tot;
E[tot]=(Node){v,c,w,head[u]};
head[u]=tot;
}
void link(int u,int v,int c,int w)
{
ADD(u,v,c,-w);
ADD(v,u,0,w);
}
void work1()
{
tot=1;memset(head,0,sizeof(head));
S=2*cnt+1;T=S+1;
for(int i=1;i<=m;++i)
for(int j=1;jQ;Q.push(S);
far[S]=0;
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head[now];e;e=E[e].next)
{
if(E[e].C && far[now]+E[e].val
传送门
讲真这道题我一时想不出网络流怎么做... ... 满脑子里都是广搜。
但广搜真的好写又好想... ... 那就懒得用网络流算了吧!
转移用二进制计算维护。
与或非啊异或啊
感受感受吧
#include
#include
#include
#include
#include
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MID int mid=(l+r)>>1
#define max(a,b) (a>b?a:b)
#define min(a,b) (a'9' || ch<'0'){if(ch=='-')res=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar();
return x*res;
}
void pr2(int x)
{
if(x>1)pr2(x>>1);
putchar((x&1)+'0');
}
void BFS()
{
queue Q;Q.push((1<BT[now]+T[i])
{
BT[next]=BT[now]+T[i];
Q.push(next);
}
}
}
}
}
int main()
{
n=gi();m=gi();memset(BT,127,sizeof(BT));
for(int i=1;i<=m;++i)
{
T[i]=gi();
while(1)
{
char ch=getchar();int tmp=0;
while(ch!='0' && ch!='-' && ch!='+')
ch=getchar();
while(ch=='0' || ch=='-' || ch=='+')
{
if(ch=='-')Bred[i]+=(1<10e8)printf("-1");
else printf("%d",BT[0]);
}
//2017年02月05日08:53:38止
传送门
经典的棋盘网络流。这里有个套路:对于棋盘,把它染成黑白两色(国际象棋),原点向黑点连边,白点向汇点连边,黑点向满足(有时是不满足)条件的白点连边。
这道题一看题解就知道是最小割。为什么呢?想想啊,对于这个黑白二分图,选择了左边的点,就不能选择与它相连的右边的点,反之亦然。于是就有:如果你选择了一个黑点,你就放弃了一片白点,也就放弃了一片权值。于是就相当于要你从这个联通二分图中去掉尽可能少的权值使S和T不连通。于是你就能够从冥冥之中的玄学力量中得到最小割的结论,然后你就可以去跑最大流了。
#include
#include
#include
#include
#include
#include
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define fa (x >> 1)
#define MID int mid=(l+r)>>1
using namespace std;
struct Node{int to,C,next;}E[20100];
int n,m,head[1010],S,T,cnt,tot,dep[1010],num[31][31],sum;
int gx[]={0,-1,0,0,1};
int gy[]={0,0,-1,1,0};
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
LL gL()
{
LL x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int c)
{
++tot;
E[tot]=(Node){v,c,head[u]};
head[u]=tot;
}
void link(int u,int v,int c)
{
ADD(u,v,c);
ADD(v,u,0);
if(u==S || v==T)sum+=c;
}
bool BFS()
{
memset(dep,0,sizeof(dep));
queue Q;
Q.push(S);
dep[S]=1;
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int e=head[now];e;e=E[e].next)
{
if(E[e].C && !dep[E[e].to])
{
dep[E[e].to]=dep[now]+1;
Q.push(E[e].to);
}
}
if(dep[T])return true;
}
return false;
}
int dfs(int now,int val)
{
if(now==T)return val;
int aug=0,c;
for(int e=head[now];e;e=E[e].next)
if(E[e].C && dep[E[e].to]==dep[now]+1)
{
c=dfs(E[e].to,min(val-aug,E[e].C));
E[e].C-=c;
E[e^1].C+=c;
aug+=c;
if(aug==val)return val;
}
return aug;
}
int search()
{
int Ans=0;
while(BFS())
Ans+=dfs(S,1010);
return Ans;
}
int main()
{
n=gi();m=gi();tot=1;
S=n*m+1;T=S+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
num[i][j]=++cnt;
if((i+j)%2==0)
link(S,cnt,gi());
else
link(cnt,T,gi());
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if((i+j)%2==0)
for(int k=1;k<=4;++k)
{
int xx=i+gx[k],yy=j+gy[k];
if(xx>0 && yy>0 && xx<=n && yy<=m)
link(num[i][j],num[xx][yy],214748364);
}
printf("%d\n",sum-search());
return 0;
}
//2017年02月05日10:23:03止
传送门
随着我渐渐不那么naive,网络流似乎也变得和蔼可亲了起来。我开始懒得裂点,沉迷于点多边少的图中,却想不到跑的比印度记者还要慢,这一切都显得是那么的玄学啊!
下面讲题解。
我觉得题面已经把连边讲的十分清楚了。比较值得一提的就是关于一个点可以被经过多次但只能取到一次值的套路。我写的是懒人连边: 两个点之间连两条边,一条有流量限制,有边权;另一条没有流量限制,但不能获得价值。(OI告诉我们,免费的一般都不是好东西)。然后神犇QT告诉我可以裂点,但本质上思想是一样的。但令我很不满的是,在点和边都相对较少的情况下,跑的竟然接么慢!枉为香港记者!
#include
#include
#include
#include
#include
#include
#define LL long long int
using namespace std;
const int N = 1100;
const int Inf = 214748364;
struct Node{int to,C,val,next;}E[N*N];
int head[N],tot=1,far[N],cnt,Ans,mark[N][N],S,T,up[N],In[N];
int a,b,P,Q;
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
void ADD(int u,int v,int c,int w)
{
++tot;
E[tot]=(Node){v,c,w,head[u]};
head[u]=tot;
}
void link(int u,int v,int c,int w)
{
ADD(u,v,c,-w);
ADD(v,u,0,w);
}
void BFS()
{
memset(far,127/3,sizeof(far));
memset(up,0,sizeof(up));
memset(In,0,sizeof(In));
queueQ;Q.push(S);
far[S]=0;In[S]=1;
while(!Q.empty())
{
int now=Q.front();Q.pop();In[now]=0;
for(int e=head[now];e;e=E[e].next)
{
if(E[e].C>0&&far[now]+E[e].val