HDU 4307 Contest 1

http://www.cnblogs.com/staginner/archive/2012/08/13/2636826.html

自己看过后两周吧,重新写了一遍。很受启发的。对于0、1,可以使用最小割的思想来做,以前有听说过0、1规划的问题,估计就是这样的了。对这个题目使用最小割 ,是一个非常巧妙的思想。

我把别人的题解复制过来吧。

从本质上讲,之所以能够用最大流解决这个问题,关键在于最大流可以求解下面这个函数的最小值:

    

    接下来就分析一下如何用最大流求解上面这个函数的极值。

    首先xi一共只有两种选择,那么最终可以按xi的取值将xi划分成两个集合,那么如果xi在值为1的集合里,xj在值为0的集合里,那么就会产生一个代价cij。同时如果xi选择0就会产生一个bi的代价,如果xi选择1就会产生一个ai的代价。

    于是构造一个源点S,汇点T做最小割,不妨假设做完最小割之后值为1的xi的集合是和S相连的部分,值为0的xi的集合是和T相连的部分。

    由于表达式中有三项,我们用三种割边来分别描述这三项的值。一种是xi选择了1,这样就不能选择0,需要把xi-T这条边割掉,由于xi选择1会产生ai的代价,那么就把这条边的容量设为ai。另一种是xi选择了0,这样就不能选择1,需要把S-xi这条边割掉,由于xi选择0会产生bi的代价,那么就把这条边的容量设为bi。最后一种是xi选择了1,xj选择了0,这样xi和xj不能在同一个集合中,需要把xi-xj这条边割掉,由于xi选择1,xj选择0产生cij的代价,那么就把这条边的容量设为cij。

    这样对建好的图做最小割就可以得到上面哪个函数的最小值。

    接着我们分析这个题目如何转化成上面这种模型。

    首先我们将D的表达式赤裸裸地写出来:

    

    这种形式必然不能看出来和上面那个表达式有什么关系,于是我们继续将其化简:

    

    如果令f等于最后一行括号里的内容,那么发生了什么?如果ai选择0会产生sum{bij}(1<=j<=N)的代价,如果ai选择1会产生ci的代价,如果ai选择1且aj选择0就会产生bij的代价。这样就完全转化成了上面的模型,具体的做法就不再重复说明了。

 

两周后自己再写的代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define LL __int64
using namespace std;

const int MAXN=1050;
const int MAXM=2100000;
const LL INF=0x7fffffff;

struct Node
{
    int from,to,next;
    LL cap;
}edge[MAXM];
int tol;
int dep[MAXN];
int head[MAXN];

int n;
void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,LL w)
{
    edge[tol].from=u;
    edge[tol].to=v; edge[tol].cap=w;  edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].from=v;
    edge[tol].to=u;
    edge[tol].cap=0;
    edge[tol].next=head[v];
    head[v]=tol++;
}

int BFS(int start,int end)
{
    int que[MAXN];
    int front,rear; front=rear=0;
    memset(dep,-1,sizeof(dep));
    que[rear++]=start;
    dep[start]=0;
    while(front!=rear)
    {
        int u=que[front++];
        if(front==MAXN)front=0;
        for(int i= head[u];i!=-1; i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].cap>0&& dep[v]==-1)
            {
                dep[v]=dep[u]+1;
                que[rear++]=v;
                if(rear>=MAXN) rear=0;
                if(v==end)return 1;
            }
        }
    }
    return 0;
}
LL dinic(int start,int end)
{
    LL res=0;
    int top;
    int stack[MAXN];
    int cur[MAXN];
    while(BFS(start,end))
    {
        memcpy(cur,head, sizeof(head));
        int u=start;
        top=0;
        while(1)
        {
            if(u==end)  
            {
                LL min=INF;
                int loc;
               for(int i=0;i<top;i++)

                  if(min>edge[stack[i]].cap)
                  {
                      min=edge[stack[i]].cap;
                      loc=i;
                  }
                for(int i=0;i<top;i++){
                    edge[stack[i]].cap-=min;
                    edge[stack[i]^1].cap+=min;
                }
                res+=min;         
                top=loc;               
                u=edge[stack[top]].from;
            }
            for(int i=cur[u]; i!=-1; cur[u]=i=edge[i].next)      
              if(edge[i].cap!=0 && dep[u]+1==dep[edge[i].to])
                 break;
            if(cur[u]!=-1)
            {
                stack [top++]= cur[u];
                u=edge[cur[u]].to;
            }
            else
            {
                if(top==0) break;
                dep[u]=-1;
                u= edge[stack[--top]].from;  
            }
        }
    }
    return res;
}


int main(){
	int T,n,x,start=0,ent=1049;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		init();
		LL sum=0,a=0;
		for(int i=1;i<=n;i++){
			a=0;
			for(int j=1;j<=n;j++){
				scanf("%d",&x);
				a+=x;
				addedge(i,j,(LL)x);
			}
			addedge(start,i,a);
			sum+=a;
		}
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			addedge(i,ent,(LL)x);
		}
		printf("%I64d\n",sum-dinic(start,ent));
	}
}

  

posted @ 2014-11-10 09:39  chenjunjie1994  阅读(235)  评论(0编辑  收藏  举报