HDU 6126 Give out candies (贼巧妙的最小割)

来源:互联网 发布:查看占用80端口的进程 编辑:程序博客网 时间:2024/06/04 18:48

思路:

最小割模型。

考虑如何转化成最小割模型。
先不考虑k个限制条件。
我们要求的是每个孩子的最大的满足度,而最小割,割的是最小容量的边,所以我们需要负权建边,然而板子跑不了负的容量啊,所以我们对于每条边加上一个足够大的值F(题目中虽然没给wij 的值,但官方题解中明确了小于1000,所以题面应该是有着问题的。),如下图方式建图,把每个孩子得到的糖数串起来,跑到的最小割的时候割出来的边减F取反就是答案了。
(建图的详细方法:对于第i个人,s连点(i,1),点(i,1)连点(i,2)。。。。点(i,m)连t)
这里写图片描述

然后考虑增加约束的情况。对于约束 x,y,z,要求第x个孩子拿到的糖数-第y个孩子拿到的糖数 <= z。
如果x=1,y = 2,z = 1,那么如果第一个孩子选了3个糖,那么第二个孩子就要至少选3-1个糖。推广:第x个孩子选了i个糖之后,第y个孩子就一定要至少选 i-z 个糖(z是负数也没关系,超出去的话就连汇点。i-z超出去的话就连源点。)。而这种约束是如何通过最小割模型实现的呢?如下图:

这里写图片描述

图中的1,2,3,4代表着弧,考虑如果我们要选的点在2上,即割边(这条路上最小容量的边)在2上,那么另一个孩子的割边一定在4上。因为如果在3上的话,我们一定可以从inf这条边的得到额外的流量,使得流量汇聚到4中,使得4中有着割边。而如果我们要选的点1上的话,同理判断,如果1的左边没有inf边,那么证明选1的时候3,4上的点都可以选,即:不存在这个约束条件。

最后来考虑无解的情况,如下图:
这里写图片描述

注意:1,2,3,4表示的弧变了。当我们选1上的点的时候,下边只能选3上的点。当我们选2上的点的时候,下边只能选4上的点。而如果3上没有点,那么1这段弧上的点就都选不了了。所以无解的情况你应该可以大致猜一下了,判断从源点到汇点是否有大于等于inf的流量,有的话就说明找不到满足条件的两个点使得割最小。(如果按照我这张图,1左边那段弧和3左边那段弧里有可以选的点的话,那么就是有解的,而约束成无解的方法和1,3的一样。最终都是要从起点开始约束到终点,这样才会有inf流量)

#include<iostream>#include<cstdio>#include<queue>#include<cstring>#include<map>#define inf 0x3f3f3f3fLLtypedef long long int lli;using namespace std;const lli maxn = 20000;struct edge{    lli to,v,next;}ed[maxn+2000000];lli d[maxn],cur[maxn],pre[maxn],gap[maxn],q[maxn+2000],cnte,head[maxn];void ae(lli x, lli y, lli v) {    ed[cnte].to = y;    ed[cnte].v = v;    ed[cnte].next = head[x];    head[x] = cnte++;    ed[cnte].to = x;    ed[cnte].v = 0;    ed[cnte].next = head[y];    head[y] = cnte++;}void rbfs (lli s,lli t) {    lli fi,se;    memset(gap,0,sizeof(gap));    memset(d,-1,sizeof(d));    d[t] = 0;    gap[0] = 1;    fi = se = 0;    q[se++] = t;    while (fi != se) {        lli u = q[fi++];        for (lli i=head[u];~i;i=ed[i].next) {            lli v = ed[i].to;            if (~d[v]) continue;            d[v] = d[u] + 1;            q[se++] = v;            gap[d[v]]++;        }    }}lli isap(lli s,lli t){    memcpy(cur,head,sizeof(head));    rbfs (s,t);    lli flow = 0, u = pre[s]=s,i;    while(d[t] < t+1) {        if(u==t) {            lli f = inf,neck;            for(i= s;i != t;i = ed[cur[i]].to){                if(f > ed[cur[i]].v){                    f = ed[cur[i]].v;                    neck = i;                }            }            for(i = s;i != t;i = ed[cur[i]].to){                ed[cur[i]].v -= f;                ed[cur[i]^1].v += f;            }            flow += f;            u = neck;        }        for(i = cur[u];~i;i=ed[i].next) if(d[ed[i].to]+1 == d[u] && ed[i].v) break;        if(~i) {            cur[u] = i;            pre[ed[i].to] = u;            u = ed[i].to;        }        else{//否则回退,重新找增广路            if(gap[d[u]] == 0 || 0 == (--gap[d[u]])) break;//GAP间隙优化,如果出现断层,可以知道一定不会再有增广路了            lli mind = t+1;            for(i = head[u];~i;i = ed[i].next){                if(ed[i].v && mind > d[ed[i].to]){                    cur[u] = i;                    mind = d[ed[i].to];                }            }            d[u] = mind + 1;            gap[d[u]]++;            u = pre[u];        }    }    return flow;}void ini(){    memset(head,-1,sizeof(head));cnte = 0;}int t,n,m,k,v;int ys(int i,int j){//映射关系    if(j == m+1) return n*m+1;    return (i-1)*m+j;}int main(){    scanf("%d",&t);    while(t--){        ini();        scanf("%d%d%d",&n,&m,&k);        int s = 0,t = n*m+1;        for(int i = 1;i <= n;i++){            ae(s,ys(i,1),inf);            for(int j = 1;j <= m;j++){                scanf("%d",&v);                ae(ys(i,j),ys(i,j+1),3000-v);            }        }        int a,b,c;        for(int i = 1;i <= k;i++){            scanf("%d%d%d",&a,&b,&c);            for(int j = 1;j <= m;j++){                if(j-c < 1) ae(ys(a,j),s,inf);                else if(j-c <= m) ae(ys(a,j),ys(b,j-c),inf);                else ae(ys(a,j),t,inf);            }        }        lli ans = isap(s,t);        if(ans >= inf){            puts("-1");        }        else{            printf("%lld\n",n*3000-ans);        }    }    return 0;}
原创粉丝点击