ZOJ - 3229 Shoot the Bullet (有源汇点上下界最大流)
题意:要在n天里给m个女生拍照,每个女生有拍照数量的下限Gi,每天有拍照数量的上限Di,每天当中每个人有拍照的上限Lij和Rij。求在满足限制的基础上,所有人最大能拍多少张照片。
分析:抛开限制,显然是一道最大流的问题,需要新建虚拟源点s和虚拟汇点t。加上上下界限制后就是有源汇点的上下界最大流问题。
要求解这个最大流,首先得保证上下界可行流有解。新增一个超级源点ss,超级汇点tt。点[1,n]视作n天,[n+1,n+m]视作m个女生。由源点s向[1,n]分别建容量为Di的弧,由于下界是0,所以不用分离出来。由[n+1,n+m]向汇点t建容量为INF - Gi的边,表示其下界为Gi,上界为正无穷。对于天数i中女生j的上下界Lij与Rij,从i向j+n建容量为Rij-Lij的弧。
记录每个点(包括s和t)的入流-出流的值cap[i],若cap[i]>0,由超级源点ss向其建容量为cap[i];若cap[i]<0,由该点向超级汇点tt建容量为-cap[i]的边。跑出ss到tt的最大流f,若f==由ss出发的所有边的流量上限之和,则说明有可行流。
在确保有可行流的基础上,该题要求的是最大流,做法是在残余网上跑出s到t的最大流,得到的最大流即为所求答案。
本题还需输出代表每天每人拍摄照片数的流量,则该流量 = 该弧流量下界 + 第二次跑出的自由流。
#include<bits/stdc++.h>
using namespace std;
using namespace std;
typedef int LL;
const int MAXN = 1505;
const int INF = 0x3f3f3f3f;
struct ISAP{
    int n;//实际建图总点数(最好比图中总点数大一点)
    struct Edge{
        int v,next;
        LL cap,flow;
    }edge[MAXN*100];
    int cur[MAXN],pre[MAXN],gap[MAXN],path[MAXN],dep[MAXN];
    int tot=0;//实际存储总边数
    void init(int n){
        this -> n  =n;
        tot=0;
        memset(pre,-1,sizeof(pre));
    }
    void AddEdge(int u,int v,LL w,LL rw=0)//加边 单向图三个参数  双向图四个
    {
        edge[tot] = (Edge){v,pre[u],w,0};
        pre[u]=tot++;
        edge[tot] = (Edge){u,pre[v],rw,0};
        pre[v]=tot++;
    }
    bool bfs(int s,int t){//其实这个bfs可以融合到下面的迭代里,但是好像是时间要长
        memset(dep,-1,sizeof(dep));
        memset(gap,0,sizeof(gap));
        gap[0]=1;
        dep[t]=0;
        queue<int>q;
        while(!q.empty())
        q.pop();
        q.push(t);//从汇点开始反向建层次图
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=pre[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                if(dep[v]==-1&&edge[i^1].cap>edge[i^1].flow)//注意是从汇点反向bfs,但应该判断正向弧的余量
                {
                    dep[v]=dep[u]+1;
                    gap[dep[v]]++;
                    q.push(v);
                    //if(v==s)//感觉这两句优化加了一般没错,但是有的题可能会错,所以还是注释出来,到时候视情况而定
                    //break;
                }
            }
        }
        return dep[s]!=-1;
    }
    LL isap(int s,int t){
        bfs(s,t);
        memcpy(cur,pre,sizeof(pre));
        int u=s;
        path[u]=-1;
        LL ans=0;
        while(dep[s]<n){//迭代寻找增广路
            if(u==t){
                LL f=INF;
                for(int i=path[u];i!=-1;i=path[edge[i^1].v])//修改找到的增广路
                    f=min(f,edge[i].cap-edge[i].flow);
                for(int i=path[u];i!=-1;i=path[edge[i^1].v]){
                    edge[i].flow+=f;
                    edge[i^1].flow-=f;
                }
                ans+=f;
                u=s;
                continue;
            }
            bool flag=false;
            int v;
            for(int i=cur[u];i!=-1;i=edge[i].next){
                v=edge[i].v;
                if(dep[v]+1==dep[u]&&edge[i].cap-edge[i].flow){
                    cur[u]=path[v]=i;//当前弧优化
                    flag=true;
                    break;
                }
            }
            if(flag){
                u=v;
                continue;
            }
            int x=n;
            if(!(--gap[dep[u]]))return ans;//gap优化
            for(int i=pre[u];i!=-1;i=edge[i].next){
                if(edge[i].cap-edge[i].flow&&dep[edge[i].v]<x){
                    x=dep[edge[i].v];
                    cur[u]=i;//常数优化
                }
            }
            dep[u]=x+1;
            gap[dep[u]]++;
            if(u!=s)//当前点没有增广路则后退一个点
            u=edge[path[u]^1].v;
        }
        return ans;
    }
}F;
int cap[MAXN];
int id[MAXN][MAXN];
int low[MAXN][MAXN];
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int n,m,u,v,tmp;
    while(scanf("%d %d",&n,&m)==2){
        int s = 0,t = n+m+1,ss=t+1,tt = ss+1;
        F.init(tt);
        memset(cap,0,sizeof(cap));
        memset(id,0,sizeof(id));
        memset(low,0,sizeof(low));
        for(int i=1;i<=m;++i){
            scanf("%d",&tmp);
            F.AddEdge(i+n,t,INF-tmp);               //有下界
            cap[i+n]-=tmp, cap[t]+= tmp;
        }
        for(int i=1;i<=n;++i){
            int c,d;
            scanf("%d %d",&c,&d);
            F.AddEdge(s,i,d);                       //有上界
            for(int j=1;j<=c;++j){
                int L,R;
                scanf("%d %d %d",&u,&L,&R);
                u++;
                F.AddEdge(i,u+n,R-L);           //有上下界
                cap[i] -= L, cap[u+n] += L;
                low[i][u] = L;
                id[i][u] = F.tot-2;
            }
        }
        F.AddEdge(t,s,INF);
        int sum =0;
        for(int i=0;i<=t;++i){
            if(cap[i]>0){
                sum+= cap[i];
                F.AddEdge(ss,i,cap[i]);
            }
            else{
                F.AddEdge(i,tt,-cap[i]);
            }
        }
        int res = F.isap(ss,tt);
        if(res!=sum){
            printf("-1\n");
        }
        else{
            res = F.isap(s,t);
            printf("%d\n",res);
            for(int i=1;i<=n;++i){
                for(int j=1;j<=m;++j){
                    if(id[i][j]){
                        printf("%d\n",F.edge[id[i][j]].flow+low[i][j]);
                    }
                }
            }
        }
        printf("\n");
    }
    return 0;
}
ZOJ - 3229 Shoot the Bullet (有源汇点上下界最大流)的更多相关文章
- ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)
		题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 题目大意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给 ... 
- zoj 3229 Shoot the Bullet(无源汇上下界最大流)
		题目:Shoot the Bullet 收藏:http://www.tuicool.com/articles/QRr2Qb 把每一天看成一个点,每个女孩也看成一个点,增加源和汇s.t,源向每一天连上[ ... 
- ZOJ Problem Set - 3229 Shoot the Bullet 【有上下界网络流+流量输出】
		题目:problemId=3442" target="_blank">ZOJ Problem Set - 3229 Shoot the Bullet 分类:有源有汇 ... 
- ZOJ 3229 Shoot the Bullet | 有源汇可行流
		题目: 射命丸文要给幻想乡的居民照相,共照n天m个人,每天射命丸文照相数不多于d个,且一个人n天一共被拍的照片不能少于g个,且每天可照的人有限制,且这些人今天照的相片必须在[l,r]以内,求是否有可行 ... 
- ZOJ 3229 Shoot the Bullet (有源有汇有上下界最大流)
		题意:一个人要给女孩子们拍照,一共 n 天,m 个女孩子,每天他至多拍 d[i] 张照片,每个女孩子总共要被至少拍 g[i] 次.在第 i 天,可以拍 c[i] 个女孩子,c[i] 个女孩子中每个女孩 ... 
- 【有源汇上下界最大流】ZOJ 3229 Shoot the Bullet
		题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 题目大意: n天给m个女孩拍照(1<=n<= ... 
- zoj 3229 Shoot the Bullet(有源汇上下界最大流)
		Shoot the Bullethttp://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 Time Limit: 2 Second ... 
- ZOJ 3229 Shoot the Bullet [上下界最大流]
		ZOJ 3229 Shoot the Bullet 题意:此生无悔入东方 上下界最大流 spj挂掉了我也不知道对不对,把代码放这里吧以后正常了可能会评测一下 #include <iostream ... 
- Shoot the Bullet(ZOJ3229)(有源汇上下界最大流)
		描述 ensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utop ... 
随机推荐
- RabbitMQ之Exchange-4
			RabbitMQ消息模型的核心思想是生产者不会将消息直接发送给队列.生产者通常不知道消息将会被哪些消费者接收,按照刚开始里介绍的rabbitMQ中所画的,生产者不是直接将消息发送给Queue么认识会交 ... 
- 使用ADO GetChunk/AppendChunk 数据库存取二进制文件图象
			在设计数据库的过程中,我们会经常要存储一些图形.长文本.多媒体(视频.音频文件)等各种各样的程序文件,如果我们在数据库中仅存储这些文件的路径信息,尽管这可以大大地减小数据库的大小,但是由于文件存在磁盘 ... 
- 打开cmd闪退
			我们在使用电脑过程中一般会很少用到cmd命令,CMD命令窗口在一些特殊情况时我们会用到,如PING下看网络通不通.在CMD窗口里运行命令如磁盘格式转换,但是有些朋友遇到了这样的问题,在开始运行输入CM ... 
- 《C++ Primer Plus》第8章 函数探幽 学习笔记
			C++ 扩展了 C 语言的函数功能.通过将 inline 关键字用于函数定义,并在首次调用该函数前提供其函数定义,可以使得 C++ 编译器将该函数视为内联函数.也就是说,编译器不是让程序跳到独立的代码 ... 
- go练习1-翻转字符串
			//翻转字符串 func T1_1() { str := "你好helloworld!" fmt.Println("翻转前", str) var ret str ... 
- Vue基础-自定义事件的表单输入组件、自定义组件的 v-model
			Vue 测试版本:Vue.js v2.5.13 学习 Vue 的自定义事件的表单输入组件,觉得文档讲的不太细致,所以这里再细化一下: 如果不用 v-model,代码应该是这样: <myinput ... 
- CentOS7.2安装配置FTP服务器VSFTP
			1,查看系统版本 2,yum安装vsftpd yum -y install vsftpd 3,修改配置文件 vim /etc/vsftpd/vsftpd.conf local_enable=YES w ... 
- Python使用函数实现把字符串转换成整数
			需求:假设Python没有提供内置函数int如果使用函数方式实现把一串字符串转换成整数例如把字符串‘12345‘转换成整数12345 思路 1,字符串也是序列可以使用map函数处理分割成一个列表 2, ... 
- Code Forces 22B  Bargaining Table
			B. Bargaining Table time limit per test 2 seconds memory limit per test 256 megabytes input standard ... 
- 在虚拟机中的搭建Web服务器(CentOS)
			1.制作本地yum源 相关可查看:http://www.cnblogs.com/xiaomingzaixian/p/8424290.html 2.安装JDK 上传上传jdk-7u45-linux-x6 ... 
