ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)

时间:2022-04-18 13:37:11

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442

题目大意:

一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照;否则输出-1。

解题思路:

有源汇带上下界的最大流,我也是第一次写。

说下建图:

①源点S到第i天流量为D[i]的边(上界为D[i],下界为0)

②第i天向女孩连流量为r-l的边(上界为r,下界为l)

③女孩向汇点T连流量为G[i]的边(上界为INF,下界为G[i])

④汇点向源点连流量为INF的边,使其变成无源汇图。(上界为INF,下界为0)

⑤out[i]为i点的出边下界和,in[i]为i点的入边下界和,若in[i]-out[i]>0,则从附加源点SS向i连流量为in[i]-out[i]的边,

若in[i]-out[i]<0,则从i点向附加汇点TT连流量为out[i]-in[i]的边。

具体求法,就是先求出改图是否存在可行流,即求有源汇上下界可行流,通过从T->连流量为INF的边即可将图变为无源汇图,这样就可以转换为无源汇上下界可行流求解。

若存在可行流,则再求一次S->T的最大流即为答案。

(这句话从网上找的)

为什么呢?因为第一次SS->TT只是求得所有满足下界的流量,而残留网络(S,T)路上还有许多*流(没有和超级源点和超级汇点连接的边)没有流满,所有最终得到的ans=(第一次流满下界的流+第二次能流通的*流)。

说起来ZOJ真是害我不浅啊,今天刚开始写上下界的题目,ZOJ 2314 low数组开小了一直TLE。。。。 然后这题忘记输出空行一直WA。。。 然后我每次都以为是自己写的问题debug好几个小时,难受啊。

代码

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define LL long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
#define bug cout<<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"<<endl;
#define bugc(_) cout << (#_) << " = " << (_) << endl;
using namespace std;
const int N=2e3+;
const int M=1e5+;
const int INF=0x3f3f3f3f; struct node{
int to,next,flow;
}edge[M*]; int cnt,st,en;
int head[N],dep[N],out[N],in[N],low[M],G[N],D[N]; void init(){
cnt=;
memset(head,,sizeof(head));
memset(out,,sizeof(out));
memset(in,,sizeof(in));
} void link(int u,int v,int flow){
edge[cnt]=node{v,head[u],flow};
head[u]=cnt++;
edge[cnt]=node{u,head[v],};
head[v]=cnt++;
} int bfs(){
memset(dep,,sizeof(dep));
dep[st]=;
queue<int>q;
q.push(st);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=edge[i].next){
node t=edge[i];
if(t.flow&&!dep[t.to]){
dep[t.to]=dep[u]+;
q.push(t.to);
}
}
}
return dep[en];
} int dfs(int u,int fl){
if(en==u) return fl;
int tmp=;
for(int i=head[u];i&&fl;i=edge[i].next){
node &t=edge[i];
if(t.flow&&dep[t.to]==dep[u]+){
int x=dfs(t.to,min(t.flow,fl));
if(x>){
tmp+=x;
fl-=x;
t.flow-=x;
edge[i^].flow+=x;
}
}
}
if(!tmp) dep[u]=-;
return tmp;
} int dinic(int S,int T){
st=S,en=T;
int ans=;
while(bfs()){
while(int d=dfs(st,INF))
ans+=d;
}
return ans;
} int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
init();
int S=,T=n+m+,SS=n+m+,TT=n+m+;
for(int i=;i<=m;i++) scanf("%d",&G[i]);
int num=,sum=;
for(int i=;i<=n;i++){
int C;
scanf("%d%d",&C,&D[i]);
for(int j=;j<=C;j++){
int id,l,r;
scanf("%d%d%d",&id,&l,&r);
id++;
link(i,id+n,r-l);
low[++num]=l;
out[i]+=l;
in[id+n]+=l;
}
}
link(T,S,INF);
for(int i=;i<=n;i++){
link(S,i,D[i]); //D[i]是上限,下限为0,因为每天最多拍D[i]张照片
}
for(int i=;i<=m;i++){
link(i+n,T,INF);//G[i]是下限,因为每个人至少要G[i]张照片
in[T]+=G[i];
out[i+n]+=G[i];
}
for(int i=;i<=n+m+;i++){
int tmp=in[i]-out[i];
if(tmp>) link(SS,i,tmp),sum+=tmp;
else if(tmp<) link(i,TT,-tmp);
}
int ans=dinic(SS,TT);
if(ans==sum){
ans=dinic(S,T);
printf("%d\n",ans);
for(int i=;i<=num;i++){
printf("%d\n",edge[i*+].flow+low[i]);
}
}
else
puts("-1");
puts("");
}
return ;
}