有源汇有上下界的最大流

ZOJ--3229

Shoot the Bullet
Time Limit: 2 Seconds Memory Limit: 32768 KB Special Judge
Gensokyo is a world which exists quietly beside ours, separated by a mystical border. It is a utopia where humans and other beings such as fairies,youkai(phantoms), and gods live peacefully together. Shameimaru Aya is a crow tengu with the ability to manipulate wind who has been inGensokyo for over 1000 years. She runs the Bunbunmaru News - a newspaper chock-full of rumors, and owns theBunkachou - her record of interesting observations for Bunbunmaru News articles and pictures of beautifuldanmaku(barrange) or cute girls living in Gensokyo. She is the biggest connoisseur of rumors about the girls ofGensokyo among the tengu. Her intelligence gathering abilities are the best inGensokyo!
During the coming n days, Aya is planning to take many photos of m cute girls living in Gensokyo to write Bunbunmaru News daily and record at leastGx photos of girl x in total in the Bunkachou. At thek-th day, there are Ck targets, Tk1,Tk2, ..., TkCk. The number of photos of targetTki that Aya takes should be in range [Lki,Rki], if less, Aya cannot write an interesting article, if more, the girl will become angry and use herlast spell card to attack Aya. What's more, Aya cannot take more than Dk photos at the k-th day. Under these constraints, the more photos, the better.
Aya is not good at solving this complex problem. So she comes to you, an earthling, for help.
Input
There are about 40 cases. Process to the end of file.
Each case begins with two integers 1 <= n <= 365, 1 <= m <= 1000. Thenm integers, G1, G2, ..., Gm in range [0, 10000]. Thenn days. Each day begins with two integer 1 <= C <= 100, 0 <= D <= 30000. Then C different targets. Each target is described by three integers, 0 <=T < m, 0 <= L <= R <= 100.
Output
For each case, first output the number of photos Aya can take, -1 if it's impossible to satisfy her needing. If there is a best strategy, output the number of photos of each girl Aya should take at each day on separate lines. The output must be in the same order as the input. If there are more than one best strategy, any one will be OK.
Output a blank line after each case.
Sample Input
2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 3 9
1 3 9
2 3 9

2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 0 3
1 3 6
2 6 9

2 3
12 12 12
3 15
0 3 9
1 3 9
2 3 9
3 21
0 0 3
1 3 6
2 6 12

Sample Output
36
6
6
6
6
6
6

36
9
6
3
3
6
9

-1



题目大意:一个屌丝给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=(第一次流满下界的流+第二次能流通的自由流)。

#include"stdio.h"
#include"string.h"
#include"queue"
#include"stack"
#define M 1500
#define inf 100000000
using namespace std;
struct node
{
    int u,v,w,c,next;
}edge[900000];
int t,head[M],work[M],dis[M];
void init()
{
    t=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v,int w,int c)
{
    edge[t].u=u;
    edge[t].v=v;
    edge[t].w=w;
    edge[t].c=c;
    edge[t].next=head[u];
    head[u]=t++;

    edge[t].u=v;
    edge[t].v=u;
    edge[t].w=0;
    edge[t].c=c;
    edge[t].next=head[v];
    head[v]=t++;
}
int bfs(int source,int sink)
{
    memset(dis,-1,sizeof(dis));
    queue<int>q;
    q.push(source);
    dis[source]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].w&&dis[v]==-1)
            {
                dis[v]=dis[u]+1;
                q.push(v);
                if(v==sink)
                    return 1;
            }
        }
    }
    return 0;
}
int dfs(int cur,int a,int sink)
{
    if(cur==sink)return a;
    for(int &i=work[cur];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].w&&dis[v]==dis[cur]+1)
        {
            int tt=dfs(v,min(a,edge[i].w),sink);
            if(tt)
            {
                edge[i].w-=tt;
                edge[i^1].w+=tt;
                return tt;
            }
        }
    }
    return 0;
}
int Dinic(int start,int sink)
{
    int ans=0;
    while(bfs(start,sink))
    {
        memcpy(work,head,sizeof(head));
        while(int tt=dfs(start,inf,sink))
            ans+=tt;
    }
    return ans;
}
int D[444],G[1111],g[1111];
int main()
{
    int n,m,i,j;
    while(scanf("%d%d",&n,&m)!=-1)
    {
        int Di,Ci;
        init();
        int source=0;//虚拟源点
        int sink=m+n+1;//虚拟汇点
        int S=m+n+2;//超级源点
        int T=m+n+3;//超级汇点
        int gsum=0;
        int sum=0;
        memset(G,0,sizeof(G));
        memset(D,0,sizeof(D));
        for(i=1;i<=m;i++)
        {
            scanf("%d",&g[i]);
            gsum+=g[i];
            sum+=g[i];
            add(i+n,sink,inf-g[i],0);//每个女孩与sink连接
        }
        add(S,sink,gsum,0);//超级源点和虚拟汇点连接
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&Ci,&Di);
            for(j=1;j<=Ci;j++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                a++;
                add(i,a+n,c-b,c);//第i天和第j个女孩连接
                D[i]+=b;
                G[a]+=b;
            }
            add(source,i,Di,0);//源点source和第i天连接
        }
        int tt=t;
        for(i=1;i<=n;i++)
        {
            add(i,T,D[i],0);//第i天和超级汇点T连接
            sum+=D[i];
        }
        for(i=1;i<=m;i++)
        {
            add(S,i+n,G[i],0);//超级源点和第j个女孩连接
            add(i+n,T,g[i],0);//第j个女孩和超级汇点连接
        }

        add(sink,source,inf,0);//
        int ans=Dinic(S,T);
        //printf("%d %d\n",ans,sum);
        if(ans!=sum)
            printf("-1\n");
        else
        {
            t=tt;
            int SS=Dinic(source,sink);
            printf("%d\n",SS);
            for(int u=1;u<=n;u++)
            {
                stack<int>p;
                for(i=head[u];i!=-1;i=edge[i].next)
                {
                    int v=edge[i].v;
                    if(v>=n+1&&v<=m+n)
                    {
                        //printf("%d--%d\n",u,v);
                        p.push(edge[i].c-edge[i].w);//因为按照输入的顺序输出答案,而该建图方式的遍历和输入顺序刚好相反
                        SS+=edge[i].c-edge[i].w;//所以先把答案压入栈中
                    }
                }
                while(!p.empty())
                {
                    printf("%d\n",p.top());//然后从栈中调取答案
                    p.pop();
                }
            }
        }
        printf("\n");
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值