zoj3229
题目大意: 一个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的更多相关文章
- Shoot the Bullet(ZOJ3229)(有源汇上下界最大流)
描述 ensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utop ...
- 【模板】有源汇有上下界最大流(网络流)/ZOJ3229
先导知识 无源汇有上下界可行流 题目链接 https://vjudge.net/problem/ZOJ-3229 https://www.luogu.com.cn/problem/P5192 (有改动 ...
- ZOJ3229 Shoot the Bullet(有源汇流量有上下界网络的最大流)
题目大概说在n天里给m个女孩拍照,每个女孩至少要拍Gi张照片,每一天最多拍Dk张相片且都有Ck个拍照目标,每一个目标拍照的张数要在[Lki, Rki]范围内,问最多能拍几张照片. 源点-天-女孩-汇点 ...
- ZOJ3229 Shoot the Bullet(有源汇的上下界最大流)
#pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string ...
- zoj3229 Shoot the Bullet(有源汇有上下界的最大流)
题意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给给定的C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌 ...
- ZOJ3229 Shoot the Bullet
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20756 思路:就讲一下有源汇上下界最大流的做法吧!对于所有的边,就按照无源汇 ...
- ZOJ3229:Shoot the Bullet——题解
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 题目大意:射命丸文要给幻想乡的居民照相,共照n天m个人,每天射命丸文 ...
- ZOJ3229 Shoot the Bullet [未AC]
Time Limit: 2 Seconds Memory Limit: 32768 KB Special Judge Gensokyo is a world which exist ...
- zoj3229 有源汇上下界最大流
题意:有一个人每天给妹子拍照,每个妹子有最少拍照数,每天有最大拍照数,每天只能给某些特定的妹子拍照,求最大拍照数 题解:很容易看出来的有源汇上下界最大流,对于有源汇 的上下界最大流,我们按照无源汇的操 ...
随机推荐
- javascript第四课变量作用域
局部变量: function f1() { var n1=0; //局部变量 n1=10; //全局变量,当前页面均可调用 } n1=10;//全局变量 var n1=10;//全局变量 在方法内的 ...
- 如何用SQL操作数据------告别标题党
额,首先跟大家道一个歉,由于本人上次利用标题来骗访问,对各位大哥大姐,叔叔阿姨,弟弟妹妹,and舅子老表的时间及流量造成了严重的浪费,本人深表歉意(好吧,其实本人内心还是有那么一丢丢的自豪的,毕竟是一 ...
- mysql存储过程和触发器的应用
***********[mysql 存储过程和触发器 -- 别安驹]********************* 1.什么情况下使用存储过程? 完成一些比较麻烦的逻辑,比如多表在mysql端的cpu很空 ...
- substr(dirname(__FILE__))
这是discuz中定义论坛安装根目录的一个常量.现在我们就来分析一下这个很简单但是非常实用的常量. define('DISCUZ_ROOT', substr(dirname(__FILE__) ...
- 【算法】超大数组去重(Java语言实现)
要求时间复杂度和空间复杂度尽可能低! 情景一:需要返回的是不重复的数组. 情景二:只需要返回不重复的元素个数.
- echarts 地图与时间轴混搭
//常量定义public class Constant { public static Integer PM_YEAR_NO = 5; } //action public class ZhiBiaoP ...
- 13.java.lang.NoSuchFiledException
java.lang.NoSuchFiledException 方法不存在异常 当程序试图通过反射来创建对象,访问(修改或读取)某个filed,但是该filed不存在就会引发异常
- 百度地图Label 样式:label.setStyle
创建文本标注对象设置样式的时候,其中的backgroundColor属性居然还支持透明啊,不过改变数值好像对效果没有影响 var numLabel = new BMap.Label(num); num ...
- 采购件不允许挂BOM
应用 Oracle Bill Of Materiel 层 Level Function 函数名 Funcgtion Name BOM_BOMFDBOM 表单名 Form Name BOMFDBOM ...
- 与ARM7相比Cortex-M3优势明显