sgu194
题意:无源无汇有上下界可行流构造
解法:建立超级源点S和超级汇点T
1.S指向每个点,权值是进入每个点的下界和
2.每个点指向T,权值是从每个点出去的权值和
3.每条边的权值改为上界-下界
求S->T的最大流,判断从S出发的边是否满流,如果满流,则存在可行流。
一个可行流为u->v的流量+u->v的下界
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 200+10;
const int maxm = 40000+10;
int n,m;
struct node{
int v,nxt,cap,flow;
}e[maxm<<2];
int head[maxn],tot,S,T;
int in[maxn],out[maxn];
int down[maxm];
void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}
int d[maxn],used[maxn];
bool BFS(){
queue<int> q;
for(int i=S;i<=T;i++) d[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1,q.push(v);
}
}
}
return d[T]!=-1;
}
int DFS(int u,int a){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;i=used[u]=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(!a) break;
}
}
return f;
}
int mf(int S,int T){
int ans=0;
while(BFS()){
for(int i=S;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF);
}
return ans;
}
int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
memset(head,-1,sizeof(head)); tot=0;
S=0,T=n+1;
memset(in,0,sizeof(in)); memset(out,0,sizeof(out));
for(int i=1;i<=m;i++){
int u,v,l,r;
scanf("%d%d%d%d",&u,&v,&l,&r);
in[v]+=l,out[u]+=l,down[i]=l;
add(u,v,r-l),add(v,u,0);
}
for(int i=1;i<=n;i++){
if(in[i]>0) add(S,i,in[i]),add(i,S,0);
if(out[i]>0) add(i,T,out[i]),add(T,i,0);
}
mf(S,T);
bool flag=true;
for(int i=head[S];i!=-1;i=e[i].nxt){
if(e[i].cap-e[i].flow>0){
flag=false; break;
}
}
if(!flag) printf("NO\n");
else{
printf("YES\n");
for(int i=0;i<2*m;i+=2) printf("%d\n",e[i].flow+down[i/2+1]);
}
}
return 0;
}
zoj3229
题意:n天分别给m个人拍照,并给出一些限制条件
1.每天只能给特定的Ki个人拍照,且张数给定[Lij,Rij]
2.每天拍照总数为Di
3.每个人n天共拍照张数不少于Gi
求n天最多拍多少张照片?
解法:m个人的照片分配到n天拍摄,建立含源点和汇点的二分图。左边m个点表示m个人,右边n个点表示n天,每个人每天分配照片数量有上下界,每个人/每天的照片总数也有上下界,因此题目划归为有上下界有源汇最大流
1.建立原图+add(T,S,INF),add(S,T,0)
2.添加附加源点汇点st和ed,如上题建图
3.mf(st,ed),判断是否存在可行流
4.head[st]=head[ed]=-1(删除附加源点和汇点),mf(S,T),即为最大流答案
5.还原最大流与上题还原可行流类似
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 2000+10;
const int maxm = 1000000+10;
int n,m;
int in[maxn],out[maxn],down[maxm];
struct node{
int v,nxt,cap,flow;
}e[maxm];
int head[maxn],tot,S,T;
int st,ed;
int dd[maxn];
void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}
int d[maxn],used[maxn];
bool BFS(int S,int T){
queue<int> q;
for(int i=0;i<=T;i++) d[i]=-1;
d[S]=0,q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(d[v]==-1&&e[i].cap-e[i].flow>0){
d[v]=d[u]+1,q.push(v);
}
}
}
return d[T]!=-1;
}
int DFS(int u,int a,int T){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow),T))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(!a) break;
}
}
return f;
}
int mf(int S,int T){
int ans=0;
while(BFS(S,T)){
for(int i=0;i<=T;i++) used[i]=head[i];
ans+=DFS(S,INF,T);
}
return ans;
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
S=0,T=n+m+1;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(head,-1,sizeof(head)); tot=0;
for(int i=1;i<=m;i++){
int g; scanf("%d",&g);
add(S,i,INF-g),add(i,S,0);
in[i]+=g,out[S]+=g;
}
int cnt=0,cnt2=tot;
for(int i=1;i<=n;i++){
int c,u,l,r;
scanf("%d%d",&c,&dd[i]);
for(int j=1;j<=c;j++){
scanf("%d%d%d",&u,&l,&r); u++;
down[cnt++]=l,in[i+m]+=l,out[u]+=l;
add(u,i+m,r-l),add(i+m,u,0);
}
}
for(int i=1;i<=n;i++){
add(i+m,T,dd[i]),add(T,i+m,0);//无下界,不需要加入out和in数组中
}
add(T,S,INF),add(S,T,0);
st=T+1,ed=T+2;
int sum=0;
for(int i=S;i<=T;i++){
if(in[i]>0) sum+=in[i],add(st,i,in[i]),add(i,st,0);
if(out[i]>0) add(i,ed,out[i]),add(ed,i,0);
}
int ans=mf(st,ed);
if(ans!=sum){
printf("-1\n");
}
else{
head[st]=head[ed]=-1;//删除超级源点和超级汇点
printf("%d\n",mf(S,T));
for(int i=0;i<cnt;i++){
printf("%d\n",e[cnt2+2*i].flow+down[i]);
}
}
printf("\n");
}
return 0;
}
sgu176
题意:有上下界有源有汇最小流
解法:
1.建立原图(容量为上界-下界)
2.添加附加源点和汇点,建立这两点到各点的边
3.求ans1=mf(附加源点,附加汇点)
4.添边汇点->源点,容量INF
5.求ans2=mf(附加源点,附加汇点)
6.判断ans1+ans2与附加汇点流量是否相等,不等无可行流,否则存在可行流。且最小流为ans2,流量即为原图边的流量+流量下界
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 200+10;
const int maxm = 10000+10;
const int INF = 0x3f3f3f3f;
int n,m;
int in[maxn],out[maxn],down[maxm],sum;
struct node{
int v,nxt,cap,flow;
}e[maxm<<2];
int head[maxn],tot;
int d[maxn],used[maxn];
int S,T,st,ed;
void add(int u,int v,int cap){
e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}
bool BFS(int S,int T,int num){
queue<int> q;
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!=-1;i=e[i].nxt){
int v=e[i].v;
if((!d[v])&&e[i].cap>e[i].flow) d[v]=d[u]+1,q.push(v);
}
}
return d[T];
}
int DFS(int u,int a,int T){
if(u==T||a==0) return a;
int f=0,m;
for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
int v=e[i].v;
if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow),T))>0){
e[i].flow+=m,e[i^1].flow-=m;
f+=m,a-=m;
if(!a) break;
}
}
return f;
}
int mf(int S,int T,int num){
int ans=0;
while(BFS(S,T,num)){
for(int i=0;i<=num;i++) used[i]=head[i];
ans+=DFS(S,INF,T);
}
return ans;
}
int main(){
//freopen("a.txt","r",stdin);
scanf("%d%d",&n,&m);
S=1,T=n,st=n+1,ed=n+2;
memset(head,-1,sizeof(head)); tot=0;
memset(in,0,sizeof(in)),memset(out,0,sizeof(out));
for(int i=1;i<=m;i++){
int u,v,c,d;
scanf("%d%d%d%d",&u,&v,&c,&d);
if(d) in[v]+=c,out[u]+=c,down[i]=c,add(u,v,0),add(v,u,0);
else add(u,v,c),add(v,u,0),down[i]=0;
}
sum=0;
for(int i=1;i<=n;i++){
if(in[i]>0) add(st,i,in[i]),add(i,st,0),sum+=in[i];
if(out[i]>0) add(i,ed,out[i]),add(ed,i,0);
}
int ans1=mf(st,ed,ed);
add(T,S,INF),add(S,T,0);
int ans2=mf(st,ed,ed);
if(ans1+ans2!=sum){
printf("Impossible\n"); return 0;
}
printf("%d\n",ans2);
for(int i=0;i<2*m;i+=2){
if(i!=2*m-2) printf("%d ",e[i].flow+down[i/2+1]);
else printf("%d\n",e[i].flow+down[i/2+1]);
}
return 0;
}