zoj3229 Shoot the Bullet (有源汇最大流)
题目大意:文文要给幻想乡的女♂孩子们拍照,一共n天,m个女♂孩子,每天文文至多拍D[i]张照片,每个女♂孩子总共要被文文至少拍G[i]次。在第i天,文文可以拍c[i]个女♂孩子,c[i]个女♂孩子中每个女♂孩子在当天被拍的次数是[li,ri],求最多可以拍多少张照片,以及每天每个可以拍的女♂孩子被拍了多少张照片。
然后就很容易想到一个网络流模型:
n天放左部,m个人放右部,源点向每天连边,容量D[i],m个人向汇点连边,容量G[i],左部向右部连边,容量是它们的ri−li,这是有源汇的上下界最大流问题…
首先先做一个有源汇上下界可行流,然后再从残量网络上跑一个s到e的最大流就是答案。
题解
首先S向每天连[0,day]的边,每一天与拍照的妹子连[l,r]的边,每个女孩和汇连[g,oo]的边
对于有源和汇的上下界网络流,只要T到S连一条[0,inf]的边,那么原图成为一个无源点汇点的循环流图,那么新建SS和TT,向每个点连边(即每一点的入流为正则源向其连,否则向汇连),求SS到TT的最大流判断满流
判断有解之后求最大流再做一次S到T的最大流
同时要去掉原来T到S的[0,inf]的边
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1000000000
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x*=;x+=ch-'';ch=getchar();}
return x*f;
}
int n,m,cnt,ans;
int head[],cur[],h[],q[],in[];
int day[],low[];
struct data{int to,next,v;}e[];
void ins(int u,int v,int w)
{e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].v=w;}
void insert(int u,int v,int w)
{ins(u,v,w);ins(v,u,);}
bool bfs(int S,int T)
{
memset(h,-,sizeof(h));
int t=,w=;q[]=S;h[S]=;
while(t!=w)
{
int now=q[t];t++;
for(int i=head[now];i;i=e[i].next)
if(e[i].v&&h[e[i].to]==-)
{
h[e[i].to]=h[now]+;
q[w++]=e[i].to;
}
}
if(h[T]==-)return ;
return ;
}
int dfs(int x,int f,int T)
{
if(x==T)return f;
int w,used=;
for(int i=cur[x];i;i=e[i].next)
if(h[e[i].to]==h[x]+)
{
w=f-used;w=dfs(e[i].to,min(e[i].v,w),T);
e[i].v-=w;if(e[i].v)cur[x]=i;e[i^].v+=w;
used+=w;if(used==f)return f;
}
if(!used)h[x]=;
return used;
}
void dinic(int S,int T)
{while(bfs(S,T)){for(int i=;i<=n+m+;i++)cur[i]=head[i];dfs(S,inf,T);}}
bool jud()
{
int SS=n+m+,TT=n+m+;
for(int i=;i<=n+m+;i++)
{
if(in[i]>)insert(SS,i,in[i]);
if(in[i]<)insert(i,TT,-in[i]);
}
dinic(SS,TT);
for(int i=head[SS];i;i=e[i].next)
if(e[i].v)return ;
return ;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
cnt=;int S=,T=n+m+;
ans=;
memset(head,,sizeof(head));
memset(in,,sizeof(in));
for(int i=;i<=m;i++)
{
int g=read();
in[T]+=g;
in[i+n]-=g;
}
int ed=;
for(int i=;i<=n;i++)
{
int x=read();day[i]=read();
for(int j=;j<=x;j++)
{
int num=read();
low[++ed]=read();int r=read();
insert(i,n+num+,r-low[ed]);
in[i]-=low[ed];
in[n+num+]+=low[ed];
}
}
for(int i=;i<=n;i++)insert(S,i,day[i]);
for(int i=;i<=m;i++)insert(i+n,T,inf);
insert(T,S,inf);
if(jud())
{
dinic(S,T);insert(T,S,-inf);//掉原来T到S的[0,inf]的边
for(int i=head[S];i;i=e[i].next)
ans+=e[i^].v;
printf("%d\n",ans);
for(int i=;i<=ed;i++)
printf("%d\n",e[(i<<)^].v+low[i]);
}
else printf("-1\n");
puts("");
}
return ;
}
然后来建图:引入源点S汇点T,n天一天一点,m个少女每人一点。S到每一天连一条边,上界为Dt,下界为0。每个少女到汇点连一条边,上界为无穷大,下界为Gi。对每一天,连一条边到所有要拍照的少女,下界为L,上界为R。然后引入超级源点SS,超级汇点TT,像无源无汇上下界可行流那样建边,然后T到S连一条边,容量为无穷大。最后从源点S到汇点T跑一遍最大流就是答案,每条边容量的取法和无源无汇上下界可行流一样。
小证明:原图为什么是那样建的我就不解释了。
至于加入超级源点和超级汇点之后为什么要连一条边T→S呢,因为根据无源无汇上下界可行流的做法,原图无源无汇,是应该有一个环的,而现在没有……
之后再从S到T跑一遍,会不会影响之前的流量下界呢?满足下界是来自于点与超级源点和超级汇点之间的边,如果我们把这两个点删掉,边的容量就没法变了,也就不会影响到流量下界。而我没有删掉,是因为若求出可行解之后,超级源点和超级汇点关联的边都应该是满流量的,再跑最大流不可能经过这两个点。
那么跑可行流的时候,和跑最大流的时候,流量是分开算的,那为什么不用加上原来可行流的流量呢?因为原先我们连了一条边T→S,根据无源无汇图中,每个点的入流等于出流,跑可行流的流量已经存在了T→S这条边中,跑最大流就时候,T→S的反向边就有了可行流的容量。所以跑最大流的时候会加上可行流的容量,就不用再加一遍了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int MAXN = ;
const int MAXE = ;
const int INF = 0x3fff3fff; struct SAP {
int head[MAXN], gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
int to[MAXE], next[MAXE], flow[MAXE], cap[MAXE];
int n, ecnt, st, ed; void init() {
memset(head, , sizeof(head));
ecnt = ;
} void add_edge(int u, int v, int c) {
to[ecnt] = v; cap[ecnt] = c; flow[ecnt] = ; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; cap[ecnt] = ; flow[ecnt] = ; next[ecnt] = head[v]; head[v] = ecnt++;
//printf("%d->%d %d\n", u, v, c);
} void bfs() {
memset(dis, 0x3f, sizeof(dis));
queue<int> que; que.push(ed);
dis[ed] = ;
while(!que.empty()) {
int u = que.front(); que.pop();
++gap[dis[u]];
for(int p = head[u]; p; p = next[p]) {
int &v = to[p];
if(cap[p ^ ] > flow[p ^ ] && dis[v] > n) {
dis[v] = dis[u] + ;
que.push(v);
}
}
}
} int Max_flow(int ss, int tt, int nn) {
st = ss, ed = tt, n = nn;
int ans = , minFlow = INF, u;
for(int i = ; i <= n; ++i) {
cur[i] = head[i];
gap[i] = ;
}
u = pre[st] = st;
bfs();
while(dis[st] < n) {
bool flag = false;
for(int &p = cur[u]; p; p = next[p]) {
int &v = to[p];
if(cap[p] > flow[p] && dis[u] == dis[v] + ) {
flag = true;
minFlow = min(minFlow, cap[p] - flow[p]);
pre[v] = u;
u = v;
if(u == ed) {
ans += minFlow;
while(u != st) {
u = pre[u];
flow[cur[u]] += minFlow;
flow[cur[u] ^ ] -= minFlow;
}
minFlow = INF;
}
break;
}
}
if(flag) continue;
int minDis = n - ;
for(int p = head[u]; p; p = next[p]) {
int &v = to[p];
if(cap[p] > flow[p] && dis[v] < minDis) {
minDis = dis[v];
cur[u] = p;
}
}
if(--gap[dis[u]] == ) break;
++gap[dis[u] = minDis + ];
u = pre[u];
}
return ans;
}
} G; int n, m, c, d, l, r, x;
int f[MAXN], down[MAXE], id[MAXE]; int main() {
while(scanf("%d%d", &n, &m) != EOF) {
G.init();
memset(f, , sizeof(f));
int s = n + m + , t = s + ;
int ss = t + , tt = ss + ;
for(int i = ; i <= m; ++i) {
scanf("%d", &x);
f[i] -= x;
f[t] += x;
G.add_edge(i, t, INF);
}
int cnt = ;
for(int i = ; i <= n; ++i) {
scanf("%d%d", &c, &d);
G.add_edge(s, i + m, d);
while(c--) {
scanf("%d%d%d", &x, &l, &r);
f[i + m] -= l;
f[x + ] += l;
down[++cnt] = l; id[cnt] = G.ecnt;
G.add_edge(i + m, x + , r - l);
}
}
int sum = ;
for(int i = ; i <= t; ++i) {
if(f[i] > ) sum += f[i], G.add_edge(ss, i, f[i]);
else G.add_edge(i, tt, -f[i]);
}
G.add_edge(t, s, INF);
if(sum != G.Max_flow(ss, tt, tt)) puts("-1");
else {
printf("%d\n", G.Max_flow(s, t, tt));
for(int i = ; i <= cnt; ++i) printf("%d\n", down[i] + G.flow[id[i]]);
}
puts("");
}
}
zoj3229 Shoot the Bullet (有源汇最大流)的更多相关文章
- ZOJ 3229 Shoot the Bullet | 有源汇可行流
题目: 射命丸文要给幻想乡的居民照相,共照n天m个人,每天射命丸文照相数不多于d个,且一个人n天一共被拍的照片不能少于g个,且每天可照的人有限制,且这些人今天照的相片必须在[l,r]以内,求是否有可行 ...
- zoj3229 Shoot the Bullet(有源汇有上下界的最大流)
题意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌 ...
- ZOJ3229 Shoot the Bullet(有源汇的上下界最大流)
#pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string ...
- Shoot the Bullet(有源汇带上下界最大流)
有源汇带上下界最大流 在原图基础上连一条汇点到源点流量为inf的边,将有源汇网络流转化为无源汇网络流用相同方法判断是否满流,如果满流再跑一边源点到汇点的最大流就是答案 例题:Shoot the Bul ...
- ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 题目大意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给 ...
- ZOJ3229 Shoot the Bullet(有源汇流量有上下界网络的最大流)
题目大概说在n天里给m个女孩拍照,每个女孩至少要拍Gi张照片,每一天最多拍Dk张相片且都有Ck个拍照目标,每一个目标拍照的张数要在[Lki, Rki]范围内,问最多能拍几张照片. 源点-天-女孩-汇点 ...
- ZOJ 3229 Shoot the Bullet (有源有汇有上下界最大流)
题意:一个人要给女孩子们拍照,一共 n 天,m 个女孩子,每天他至多拍 d[i] 张照片,每个女孩子总共要被至少拍 g[i] 次.在第 i 天,可以拍 c[i] 个女孩子,c[i] 个女孩子中每个女孩 ...
- zoj 3229 Shoot the Bullet(无源汇上下界最大流)
题目:Shoot the Bullet 收藏:http://www.tuicool.com/articles/QRr2Qb 把每一天看成一个点,每个女孩也看成一个点,增加源和汇s.t,源向每一天连上[ ...
- ZOJ3229 Shoot the Bullet
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20756 思路:就讲一下有源汇上下界最大流的做法吧!对于所有的边,就按照无源汇 ...
随机推荐
- CentOS7.4下载与安装 、使用
CentOS7.4下载与安装 1:安装步骤这篇博客挺详细的就不多说了: https://blog.csdn.net/qq_39135287/article/details/83993574 2:安装好 ...
- 【Visual Studio】 使用EF、 Linq2Sql快速创建数据交互层(一)
项目伊始,创建数据库交互层代码是底层框架的首要任务.常用的做法包括手动编码.Hibernate或者动软之类的代码生成器,而多数人忽略了.Net环境下VS提供的两套非常好用的数据层工具. EF和Linq ...
- 【转载】阿里入局,独角兽估值30亿美金,谈谈RPA是什么
缩短法定工作时间,已成国际劳动立法趋势,全球政府都曾面对这样的议题,过往企业IT也在思考这件事,开发出更好的软件系统帮助员工,就是普遍作法,这也已经行之有年,而现阶段最有效的作法,则是要用AI来提供帮 ...
- Jmeter发送SOAP请求对WebService接口测试
Jmeter发送SOAP请求对WebService接口测试 1.测试计划中添加一个用户自定义变量 2.HTTP信息头管理器,添加Content-Tpe, application/soap+xml;c ...
- stl vector创建二维数组
vector<vector<); for (auto it = v.begin(); it != v.end(); it++) { ; (*it).reserve();//预留空间为5,但 ...
- Codeforces 1197 E (dp+sort+二分) (Rust)
原题链接 2300分 大意 俄罗斯套娃,每个有内容半径in和外围半径out in_i<out_i 如果 in_i >= out_j ,那么j可以放在i内 定义残留空间 = 一列嵌套的套娃 ...
- dp(最长升序列)
http://poj.org/problem?id=2533 题意:给你n(1-1000)个数,求这n个数的最长升序列. 题解:dp[i]表示以第i个数结尾的最长升序列. #include & ...
- manacher算法学习(求最长回文子串长度)
Manacher总结 我的代码 学习:yyb luogu题目模板 xzy的模板 #include<iostream> #include<cstdlib> #include< ...
- 状压BFS
题意:1个机器人找几个垃圾,求出最短路径. 状压BFS,这道题不能用普通BFS二维vis标记数组去标记走过的路径,因为这题是可以往回走的,而且你也不能只记录垃圾的数量就可以了,因为它有可能重复走同一 ...
- Windows程序设计--(三)窗口与消息
3.1 窗口的创建 3.1.1 系统结构概述 所谓「Windows给程序发送消息」,是指Windows呼叫程序中的一个函数,该函数的参数描述了这个特定消息.这种位于Windows程序中的函数称为「窗口 ...