题目大意: 一个XX用n天要给m个女神拍写真。这n天里每个女神i分别至少要拍Gi张照片,XX在第j天会给指定Cj个女神最多拍Dj张照片,每个女神第j天拍照数在lj到hj张照片。问XX是否安排完成他的任务,不能输出-1,如果能的话,让他尽可能多拍照。先输出他最多能拍的张数,然后对于输入每一行提到要拍照的女神,按照顺序输出当天那个女神要拍照的张数。(就描述到这了,更多请访问下面的链接)

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

这是一道很典型的有源汇的上下界最大流

容我鸣谢一下先:http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html,我是看了这里报告,自己再搜索资料整理的。

再鸣谢:http://fayaa.com/code/view/26516/是这个帮助我真正理解了的代码(也不是这么说,主要是因为***),虽然这里没有任何语言描述,但博主的代码是SAP实现,容小菜我偷个懒,网络流我都倾向SAP,不习惯dinic,而其他博客一般都是dinic算法写的,读起来太累了。

好吧,还是返回主题了。通常我们求的最大流是上界最大流,而下界都是0。对于无源汇的上下界最大流,大家应该不难理解,把下界边差量du[i](一会儿解释)直接跟我们定义的源点和汇点连起来,自由边(上界边-下界边)按原图留下。现在想想,下界边一定对应一点是入、另一个点是出,若du[i] = in[i]-out[i],那么当du[i]为正时,表明对于点i,一定要流进du[i],我们连边source --> i  with capacity = du[i],否则对于点i一定要流出-du[i],我们连边 i --> sink with capacity = -du[i]。这些就是上下界的全部逻辑翻译了。如果把 sum = sigma(du[i] | du[i] > 0) (或者sum = -sigma(du[i] | du[i] < 0)),那么对构造这个图求一遍最大流,如果maxflow = sum,证明所有限制都能满足了。

当然,有的题目还有一些要求,比如这道题里的:拍照数尽可能多,这就需要后续工作了。当然对于这道题,还有一个特殊的地方,它是有自定义源汇的。那如何把变成无源汇的呢?很简单,假设ss、tt分别是题目定义的源点和汇点,连一条边 tt --> ss with capacity = infinity。这样就形成了环回的一个无源汇的网络图。另外自己再定义超级源点汇点,按照上一段描述得构图判所有限制是否能满足所有限制。

(*)若满足,重设源点、汇点为题目自定义源点、汇点,对于残余网络图求一遍最大流。最后,把网络图里的流入出情况从记录的边中读出来还原出答案就好了。

特别说明一下,有很多博客里都一再强调:对于上一段求最大流时要删掉自定义超级源点、汇点和 ”tt --> ss with capacity = infinity“这条环回边。我是怎么也理解不了了。超级源点只出不入,超级汇点只入不出,它们干那些博主们毛线事???为什么要删掉???环回边的逆向边记录有要满足所有限制必须的流量值。。。删了,你还去哪找更稳妥的这个值????也许我不了解dinic算法,没理解出这些话的强大含义。。。弱弱可以YY一下不,会不会是原始博主写这句话时是他还在学花拳绣腿的时候,而他现在忘了改过来,而千万学习者就未加思索的加上来了呢?反正我是,仅仅更改源点、汇点后,直接对残余网络求最大流AC的。

贴上代码:欢迎大神们提出珍贵建议。

 #include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = ;
const int maxe = ;
const int maxf = (-)^(<<);
#define smaller(a,b) ((a)<(b)?(a):(b))
struct edge{
int u,v,c;
edge(int a=,int b=,int d=){ u=a, v=b, c=d; }
}e[maxe];
int head[maxn];
int next[maxe];
int cnt;
void add(int u,int v,int c){
e[cnt] = edge(u,v,c);
next[cnt] = head[u], head[u] = cnt++;
//printf("cnt = %d,\t%d --> %d with c = %d, next = %d\n",cnt-1,u,v,c,next[cnt-1]);
e[cnt] = edge(v,u,);
next[cnt] = head[v], head[v] = cnt++;
}
//***********************************//
int source,sink,maxdep;
int dep[maxn],gap[maxn];
int cur[maxn],trace[maxn],que[maxn],top;
void setGD(){
for(int i=;i<=maxdep;i++) dep[i] = maxdep;
dep[sink] = ;
int front,rear,u,v;
front = rear = ;
que[rear++] = sink;
while(front != rear){
u = que[front++];
if(front >= maxn) front = ;
for(int i=head[u];i!=-;i=next[i]){
v = e[i].v;
//if(v > maxdep) continue; //这句是删掉超级源点、汇点的句子,删不删都AC。。。
if(dep[v] < maxdep) continue; //if(e[i].c || dep[v] <= dep[u]+1) continue; //不是逆向边,或者'被访问过'
dep[v] = dep[u]+;
que[rear++] = v;
if(rear >= maxn) rear = ;
}
}
memset(gap,,sizeof(gap));
for(int i=;i<=maxdep;i++) gap[dep[i]]++;
}
int maxF(){
setGD();
for(int i=;i<=maxdep;i++) cur[i] = head[i];
int u=source,flow=,i;
top = ;
while(dep[source] <= maxdep){
if(u == sink){
int tf = maxf,ins;
for(i=;i<top;i++){ //找瓶颈边
if(tf > e[trace[i]].c){
tf = e[trace[i]].c;
ins = i;
}
}
/*
for(i=0;i<top;i++)
printf("%d -> ",e[trace[i]].u);
printf("%d , temp_flow = %d\n",e[trace[top-1]].v,tf);
*/
for(i=;i<top;i++){
e[trace[i]].c -= tf;
e[trace[i]^].c += tf;
}
flow += tf;
u = e[trace[ins]].u, top = ins;
}
if(u != sink && gap[dep[u]-]==) break;
for(i=cur[u];i!=-;i=next[i])
if(e[i].c > && dep[u] == dep[e[i].v] + )
break;
if(i != -) {
trace[top++] = i;
cur[u] = i;
u = e[i].v;
}
else {
int mindep = maxdep;
for(i=head[u];i!=-;i=next[i]){
if(e[i].c && dep[e[i].v] < mindep)
mindep = dep[e[i].v], cur[u] = i;
}
gap[dep[u]]--;
dep[u] = mindep + ;
gap[dep[u]]++;
if(u != source)
u = e[trace[--top]].u;
}
}
return flow;
}
//**********************************//
int in[maxn],out[maxn],du[maxn],basement;
int lownm[][];
int order[][],oidx[];
void initial()
{
cnt = ;
memset(head,-,sizeof(head));
memset(in,,sizeof(in));
memset(out,,sizeof(out));
memset(lownm,,sizeof(lownm));
//initial source ,sink and maxdep;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) != EOF){
initial();
source = n+m+;
sink = source+;
maxdep = sink+;
int ss=n+m+;
int tt=ss+;
int fsum = ;
basement = tt;
for(int i=;i<m;i++)
scanf("%d",&out[i+n]), in[tt]+=out[i+n], add(i+n,tt,maxf-out[i+n]);
int c,d;
for(int nn=;nn<n;nn++){
scanf("%d%d",&c,&d);
oidx[nn] = c;
add(ss,nn,d);
int girl,low,up;
for(int cc=;cc<c;cc++){
scanf("%d%d%d",&girl,&low,&up);
lownm[nn][girl]=low;
add(nn,n+girl,up-low);
out[nn]+=low;
in[n+girl] += low;
order[nn][cc] = girl;
}
}
int fcnt = cnt;
add(tt,ss,maxf);
int sum = ;
for(int i=;i<=basement;i++){
du[i] = in[i]-out[i];
if(du[i] > ) add(source,i,du[i]), sum+=du[i];
else if(du[i] < ) add(i,sink,-du[i]);
}
int flow = maxF();
if(flow < sum) printf("-1\n\n");
else {
source = ss;
sink = tt;
maxdep = sink+;
//e[fcnt].c = 0, e[fcnt^1] = 0;
flow = maxF();
for(int i=;i<n;i++)
for(int j=head[i];j>=;j=next[j])
if(!(j&))
lownm[i][e[j].v-n] += e[j^].c;
//flow = 0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) flow += lownm[i][j];
printf("%d\n",flow);
for(int i=;i<n;i++)
for(int j=;j<oidx[i];j++)
printf("%d\n",lownm[i][order[i][j]]);
puts("");
}
}
return ;
}

另外在贴上一个未AC的代码,这个代码错在构图,***调了我8个小时,还是没搞定。。。

我当时的构图思路是这样的: source --> 某个女神 --> 某一天 --> 汇点。结果就弹了一天。

后来AC代码改成:source  --> 某一天 --> 某个女神--> 汇点。很怀疑是这个special judge的判题程序弱爆了。请有能力的大神给我指点迷津啊。。。

代码如下,请大神斧正!

 /*
构图。。。不解,未AC
*/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = ;
const int maxe = ;
const int maxf = (-)^(<<);
#define smaller(a,b) ((a)<(b)?(a):(b))
struct edge{
int u,v,c;
edge(int a=,int b=,int d=){ u=a, v=b, c=d; }
}e[maxe];
int head[maxn];
int next[maxe];
int cnt;
void add(int u,int v,int c){
e[cnt] = edge(u,v,c);
next[cnt] = head[u], head[u] = cnt++;
//printf("cnt = %d,\t%d --> %d with c = %d, next = %d\n",cnt-1,u,v,c,next[cnt-1]);
e[cnt] = edge(v,u,);
next[cnt] = head[v], head[v] = cnt++;
}
//***********************************//
int source,sink,maxdep;
int dep[maxn],gap[maxn];
int cur[maxn],trace[maxn],que[maxn],top;
void setGD(){
for(int i=;i<=maxdep;i++) dep[i] = maxdep;
dep[sink] = ;
int front,rear,u,v;
front = rear = ;
que[rear++] = sink;
while(front != rear){
u = que[front++];
if(front >= maxn) front = ;
for(int i=head[u];i!=-;i=next[i]){
v = e[i].v;
if(v > maxdep) continue;
if(dep[v] < maxdep) continue; //if(e[i].c || dep[v] <= dep[u]+1) continue; //不是逆向边,或者'被访问过'
dep[v] = dep[u]+;
que[rear++] = v;
if(rear >= maxn) rear = ;
}
}
memset(gap,,sizeof(gap));
for(int i=;i<=maxdep;i++) gap[dep[i]]++;
}
int maxF(){
setGD();
for(int i=;i<=maxdep;i++) cur[i] = head[i];
int u=source,flow=,i;
top = ;
while(dep[source] <= maxdep){
if(u == sink){
int tf = maxf,ins;
for(i=;i<top;i++){ //找瓶颈边
if(tf > e[trace[i]].c){
tf = e[trace[i]].c;
ins = i;
}
}
/*
for(i=0;i<top;i++)
printf("%d -> ",e[trace[i]].u);
printf("%d , temp_flow = %d\n",e[trace[top-1]].v,tf);
*/
for(i=;i<top;i++){
e[trace[i]].c -= tf;
e[trace[i]^].c += tf;
}
flow += tf;
u = e[trace[ins]].u, top = ins;
}
if(u != sink && gap[dep[u]-]==) break;
for(i=cur[u];i!=-;i=next[i])
if(e[i].c > && dep[u] == dep[e[i].v] + )
break;
if(i != -) {
trace[top++] = i;
cur[u] = i;
u = e[i].v;
}
else {
int mindep = maxdep;
for(i=head[u];i!=-;i=next[i]){
if(e[i].c && dep[e[i].v] < mindep)
mindep = dep[e[i].v], cur[u] = i;
}
gap[dep[u]]--;
dep[u] = mindep + ;
gap[dep[u]]++;
if(u != source)
u = e[trace[--top]].u;
}
}
return flow;
}
//**********************************//
int in[maxn],out[maxn],du[maxn],basement;
int lownm[][];
int order[][],oidx[];
void initial()
{
cnt = ;
memset(head,-,sizeof(head));
memset(in,,sizeof(in));
memset(out,,sizeof(out));
memset(lownm,,sizeof(lownm));
//initial source ,sink and maxdep;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m) != EOF){
initial();
source = n+m+;
sink = source+;
maxdep = sink+;
int ss=n+m+;
int tt=ss+;
int fsum = ;
basement = tt;
for(int i=;i<m;i++)
scanf("%d",&in[i]), out[ss]+=in[i], add(ss,i,maxf), fsum+=in[i];
int c,d;
for(int nn=;nn<n;nn++){
scanf("%d%d",&c,&d);
oidx[nn] = c;
int girl,low,up;
for(int cc=;cc<c;cc++){
scanf("%d%d%d",&girl,&low,&up);
lownm[nn][girl]=low;
add(girl,m+nn,up-low);
out[girl]+=low;
in[m+nn] +=low;
order[nn][cc] = girl;
}
add(m+nn,tt,d);
}
int fcnt = cnt;
add(tt,ss,maxf);
int sum = ;
for(int i=;i<=basement;i++){
du[i] = in[i]-out[i];
if(du[i] > ) add(source,i,du[i]), sum+=du[i];
else if(du[i] < ) add(i,sink,-du[i]);
}
int flow = maxF();
int flag = ;
for(int i=head[source];i>=;i=next[i])
if(e[i].c){
flag = ;
break;
}
if(!flag) printf("-1\n\n");
else {
source = ss;
sink = tt;
maxdep = sink+;
//e[fcnt].c = 0, e[fcnt^1] = 0;
flow = maxF();
for(int i=n+m;i>=m;i--)
for(int j=head[i];j>=;j=next[j])
if(j&)
lownm[i-m][e[j].v] += e[j].c;
//flow = 0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) flow += lownm[i][j];
printf("%d\n",flow);
for(int i=;i<n;i++)
for(int j=;j<oidx[i];j++)
printf("%d\n",lownm[i][order[i][j]]);
puts("");
}
}
return ;
}

zoj3229的更多相关文章

  1. Shoot the Bullet(ZOJ3229)(有源汇上下界最大流)

    描述 ensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utop ...

  2. 【模板】有源汇有上下界最大流(网络流)/ZOJ3229

    先导知识 无源汇有上下界可行流 题目链接 https://vjudge.net/problem/ZOJ-3229 https://www.luogu.com.cn/problem/P5192 (有改动 ...

  3. ZOJ3229 Shoot the Bullet(有源汇流量有上下界网络的最大流)

    题目大概说在n天里给m个女孩拍照,每个女孩至少要拍Gi张照片,每一天最多拍Dk张相片且都有Ck个拍照目标,每一个目标拍照的张数要在[Lki, Rki]范围内,问最多能拍几张照片. 源点-天-女孩-汇点 ...

  4. ZOJ3229 Shoot the Bullet(有源汇的上下界最大流)

    #pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string ...

  5. zoj3229 Shoot the Bullet(有源汇有上下界的最大流)

    题意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌 ...

  6. ZOJ3229 Shoot the Bullet

    http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20756 思路:就讲一下有源汇上下界最大流的做法吧!对于所有的边,就按照无源汇 ...

  7. ZOJ3229:Shoot the Bullet——题解

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 题目大意:射命丸文要给幻想乡的居民照相,共照n天m个人,每天射命丸文 ...

  8. ZOJ3229 Shoot the Bullet [未AC]

    Time Limit: 2 Seconds      Memory Limit: 32768 KB      Special Judge Gensokyo is a world which exist ...

  9. zoj3229 有源汇上下界最大流

    题意:有一个人每天给妹子拍照,每个妹子有最少拍照数,每天有最大拍照数,每天只能给某些特定的妹子拍照,求最大拍照数 题解:很容易看出来的有源汇上下界最大流,对于有源汇 的上下界最大流,我们按照无源汇的操 ...

随机推荐

  1. linux内核函数库文件的寻找

    linux内核函数的so库文件怎么找呢? 首先还是要产生一个进程的coredump文件的 linux有一个lib-gdb.so库,这个进程的coredump文件中所有load段的最后一个load段中, ...

  2. 调magento自定义模板发邮件

    1. 设置邮件模板 <global> <template> <email> <custom_email_template1 module="Samp ...

  3. C# 使用XmlDocument类对XML文档进行操作

    原创地址:http://www.cnblogs.com/jfzhu/archive/2012/11/19/2778098.html 转载请注明出处 W3C制定了XML DOM标准.很多编程语言中多提供 ...

  4. hadoop中汉字与英文字符混合的keyword做为combine的key的问题

    近期,须要将汉字与字符的非常合串作为combine的输出的key, 这样做是希望,利用hadoop的归并来依照key进行分组,然后,在reduce阶段,拿到的都是一个一个组. 可是,发现,这样的,汉字 ...

  5. Java提高学习之Object(4)

    哈希码 问: hashCode()方法是用来做什么的? 答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来).这个方法通常用在基于hash的集合类中,像java. ...

  6. PHP基础示例:用正则表达式修改配置信息

    各php工作原理图: 以下是扯代码时间: <?php //配置文件信息 define("HOST","localhost"); //主机名 define( ...

  7. JavaWeb核心编程之(三.5)HTTP请求和接受表单数据

    HTTP简介WEB浏览器与WEB拂去其之间的一问一答的交互过程, 必须遵循一定的规则,这个规则就是HTTP协议HTTP是hypertext transfer protocol(超文本传输协议)的简写, ...

  8. Iso language code table之(软件国际化)

    ISO 639是用来区分所有已知的语言规范的术语.每种语言都分配两个字母(639-1)或三个英文字母(639-2和639-3),小写字母的缩写,修订后的版本命名的.该系统是非常有用的语言学家和人类学家 ...

  9. Python Tensorflow下的Word2Vec代码解释

    前言: 作为一个深度学习的重度狂热者,在学习了各项理论后一直想通过项目练手来学习深度学习的框架以及结构用在实战中的知识.心愿是好的,但机会却不好找.最近刚好有个项目,借此机会练手的过程中,我发现其实各 ...

  10. AutoIt 函数学习之----WinWaitActive

    WinWaitActive函数 暂停脚本的执行直至指定窗口被激活(成为活动状态)为止. WinWaitActive ( "窗口标题"[, "窗口文本"[, 超时 ...