二分图带权最大独立集 网络流解决 hdu 1569

方格取数(2)

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3663    Accepted Submission(s): 1148

Problem Description
给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
 

 

Input
包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
 

 

Output
对于每个测试实例,输出可能取得的最大的和
 

 

Sample Input
3 3 75 15 21 75 15 28 34 70 5
 

 

Sample Output
188
 

 

Author
ailyanlu
 

 

Source
 

 

Recommend
8600
 
 
 
思路:

最大点权独立集=总权值-最小点权覆盖集

最小割=最大流

最小点权覆盖集=最小割

根据奇偶建立二分图,

if(i+j)%2==0 源点和该点连接,权值为该点的点权,

if(i+j)%2==1 该点和汇点连接,权值为该点的点权,

之后若i+j为偶数的点和i+j为奇数的点之间相邻,那么就连一条从为偶数的点到为奇数的点的边,权值为无穷大

 

之后  所有权值-最大流 即可

 

#include <stdio.h>

#include <string.h>

#define VM 2555

#define EM 2055000

#define inf 0x3f3f3f3f

struct Edge

{

    int frm,to,cap,next;

}edge[EM];



int head[VM],dep[VM],ep;     //dep为点的层次

void addedge (int cu,int cv,int cw)  //第一条边下标必须为偶数

{

    edge[ep].frm = cu;

    edge[ep].to = cv;

    edge[ep].cap = cw;

    edge[ep].next = head[cu];

    head[cu] = ep;

    ep ++;

    edge[ep].frm = cv;

    edge[ep].to = cu;

    edge[ep].cap = 0;

    edge[ep].next = head[cv];

    head[cv] = ep;

    ep ++;

}



int BFS (int src,int des)     //求出层次图

{

    int que[VM],i,front = 0,rear = 0;

    memset (dep,-1,sizeof(dep));

    que[rear++] = src;

    dep[src] = 0;

    while (front != rear)

    {

        int u = que[front++];

        front = front%VM;

        for (i = head[u];i != -1;i = edge[i].next)

        {

            int v = edge[i].to;

            if (edge[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中

            {

                dep[v] = dep[u] + 1;        //建立层次图

                que[rear ++] = v;

                rear = rear % VM;

                if (v == des)  //找到汇点 返回

                    return 1;

            }

        }

    }

    return 0;

}

int dinic (int src,int des)

{

    int i,res = 0,top;

    int stack[VM];    //stack为栈,存储当前增广路

    int cur[VM];        //存储当前点的后继 跟head是一样的

    while (BFS(src,des))   //if BFS找到增广路

    {

        memcpy (cur,head,sizeof (head));

        int u = src;       //u为当前结点

        top = 0;

        while (1)

        {

            if (u == des)     //增广路已全部进栈

            {

                int min = inf,loc ;

                for (i = 0;i < top;i ++)       //找最小的增广跟并loc记录其在stack中位置

                    if (min > edge[stack[i]].cap)  //以便退回该边继续DFS

                    {

                        min = edge[stack[i]].cap;

                        loc = i;

                    }

                for (i = 0;i < top;i ++)   //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减

                {                           //偶数是正向边,奇数是负向边,边从0开始

                    edge[stack[i]].cap -= min;

                    edge[stack[i]^1].cap += min;

                }                              //将增广路中的所有边修改

                res += min;

                top = loc;

                u = edge[stack[top]].frm;         //当前结点修改为最小边的起点

            }

            for (i = cur[u];i != -1;cur[u] = i = edge[i].next)   //找到当前结点对应的下一条边

                if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有

                    break;                                  // 1-->4 这条边满足条件 就把1到2、3的边给去掉

            if (cur[u] != -1)            //当前结点的下一条边存在

            {

                stack[top ++] = cur[u];   //把该边放入栈中

                u = edge[cur[u]].to;         //再从下个点开始找

            }

            else

            {

                if (top == 0)        //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路

                    break;

                dep[u] = -1;            //当前结点不在增广路中,剔除该点

                u = edge[stack[--top]].frm; //退栈 回朔,继续查找

            }

        }

    }

    return res;

}



int dir[4][2]={0,1,0,-1,1,0,-1,0};

int x,y;

int main ()///坐标从0或1开始均可  注意别忘记下面的2个初始化

{

    int m,n;

    int src,des;

    int sum=0;

    while (scanf ("%d %d",&m,&n)!=EOF)

    {

        sum=0;

        ep = 0;//边的初始化

        src =n*m;

        des =m*n+1;

        memset (head,-1,sizeof(head));///这里初始化

        for(int i=0;i<m;i++)

            for(int j=0;j<n;j++)

            {

                int mid;

                scanf("%d",&mid);

                sum+=mid;

                //printf("%d\n",i*m+j);

                if((i+j)%2==0) addedge(src,i*n+j,mid);

                else addedge(i*n+j,des,mid);

            }

        for(int i=0;i<m;i++)

        {

            for(int j=0;j<n;j++)

            {

                for(int k=0;k<4;k++)

                {

                   x=i+dir[k][0];

                   y=j+dir[k][1];

                   if(x>=0&&x<m&&y>=0&&y<n)

                   {

                       if(((x+y)%2)!=((i+j)%2))

                       {

                           if((x+y)%2==1)

                           addedge(i*n+j,x*n+y,inf);

                           else addedge(x*n+y,i*n+j,inf);

                       }

                   }

                }

            }

        }

       // printf("sum=%d\n",sum);

        int ans = dinic (src,des);

        printf ("%d\n",sum-ans);

    }

    return 0;

}


 

 

 

你可能感兴趣的:(HDU)