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

题意:

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

分析:

增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为oo的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边。

建图模型:源点s,终点d。超级源点ss,超级终点dd。首先判断是否存在满足所有边上下界的可行流,方法可以转化成无源汇有上下界的可行流问题。怎么转换呢?

增设一条从d到s没有下界容量,上界容量为无穷的边,那么原图就变成了一个无源汇的循环流图。接下来的事情一样,超级源点ss连i(du[i]>0),i连超级汇点(du[i]<0),

对(ss,dd)进行一次最大流,当maxflow等于所有(du[]>0)之和时,有可行流,否则没有。

当有可行流时,删除超级源点ss和超级终点dd,再对(s,d)进行一次最大流,此时得到的maxflow则为题目的解。

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

// File Name: 3229.cpp
// Author: Zlbing
// Created Time: 2013/6/30 20:44:46 #include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
using namespace std;
#define CL(x,v); memset(x,v,sizeof(x));
#define INF 0x3f3f3f3f
#define LL long long
#define REP(i,r,n) for(int i=r;i<=n;i++)
#define RREP(i,n,r) for(int i=n;i>=r;i--)
const int MAXN=;
struct Edge{
int from,to,cap,flow;
};
bool cmp(const Edge& a,const Edge& b){
return a.from < b.from || (a.from == b.from && a.to < b.to);
}
struct Dinic{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[MAXN];
bool vis[MAXN];
int d[MAXN];
int cur[MAXN];
void init(int n){
this->n=n;
for(int i=;i<=n;i++)G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,});
edges.push_back((Edge){to,from,,});//当是无向图时,反向边容量也是cap,有向边时,反向边容量是0
m=edges.size();
G[from].push_back(m-);
G[to].push_back(m-);
}
bool BFS(){
CL(vis,);
queue<int> Q;
Q.push(s);
d[s]=;
vis[s]=;
while(!Q.empty()){
int x=Q.front();
Q.pop();
for(int i=;i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=;
d[e.to]=d[x]+;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t||a==)return a;
int flow=,f;
for(int& i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(d[x]+==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>){
e.flow+=f;
edges[G[x][i]^].flow-=f;
flow+=f;
a-=f;
if(a==)break;
}
}
return flow;
}
//当所求流量大于need时就退出,降低时间
int Maxflow(int s,int t,int need){
this->s=s;this->t=t;
int flow=;
while(BFS()){
CL(cur,);
flow+=DFS(s,INF);
if(flow>need)return flow;
}
return flow;
}
//最小割割边
vector<int> Mincut(){
BFS();
vector<int> ans;
for(int i=;i<edges.size();i++){
Edge& e=edges[i];
if(vis[e.from]&&!vis[e.to]&&e.cap>)ans.push_back(i);
}
return ans;
}
void Reduce(){
for(int i = ; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
}
void ClearFlow(){
for(int i = ; i < edges.size(); i++) edges[i].flow = ;
}
}; int n,m;
Dinic solver;
int du[MAXN];
int dn[MAXN][MAXN];
int id[MAXN][MAXN];
int main()
{
while(~scanf("%d%d",&n,&m))
{
int s=,t=n+m+;
int ss=n+m+,tt=n+m+;
int a,b,c;
CL(du,);
CL(dn,);
CL(id,);
solver.init(n+m+);
REP(i,,m)
{
scanf("%d",&a);
solver.AddEdge(n+i,t,INF-a);
du[n+i]-=a;
du[t]+=a;
}
int C,D;
REP(i,,n)
{
scanf("%d%d",&C,&D);
solver.AddEdge(s,i,D);
REP(j,,C)
{
scanf("%d%d%d",&a,&b,&c);
solver.AddEdge(i,a+n+,c-b);
du[i]-=b;
du[a+n+]+=b;
dn[i][a]=b;
id[i][a]=solver.edges.size()-;
}
}
solver.AddEdge(t,s,INF);
int sum=;
REP(i,,t)
{
if(du[i]<)
{
solver.AddEdge(i,tt,-du[i]);
}
else if(du[i]>)
{
solver.AddEdge(ss,i,du[i]);
sum+=du[i];
}
}
int maxflow=solver.Maxflow(ss,tt,INF);
if(maxflow==sum)
{
int ans=solver.Maxflow(s,t,INF);
printf("%d\n",ans);
for(int i=;i<=n;i++)
{
for(int j=;j<m;j++)
if(id[i][j])
printf("%d\n",solver.edges[id[i][j]].flow+dn[i][j]);
}
}
else printf("-1\n");
printf("\n");
}
return ;
}
上一篇:Centos常用快捷键


下一篇:MPP架构海量数据分析仓库——Greenplum介绍