hdu 1569 方格取数 最大点权独立集(hdu 1565)

hdu的1565也可以用同样的代码、将m=n就可以了。其他的照样不变

题意描述:你一个m*n的格子的棋盘,每个

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

分析:该题求一个二分图的最大权独立集,那么如何建立二分图呢?考虑到最大权独立集和最小点权覆盖集是互补的,那么我们就可以将该问题转化为最小点权覆盖了,从而转换为最小割问题!下面给出两种建图的方法:

第一种:根据奇偶建立二分图,如果(i+j%2==0 那么可以是该点和源点连接,其余的和汇点连接,权值均为该点的点权,之后若i+j为偶数的点和i+j为奇数的点之间相邻,那么就连一条从为偶数的点到为奇数的点的边,权值为无穷大

第二种:拆点,将每个点权拆为两个点uu’如果两个点相邻,那么连一条边从uv’,权值为无穷大,u连接源点,权值为点权,u’连接汇点,权值也为点权

 

对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:

最大点权独立集 + 最小点权覆盖集 = 总点权和,

这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,

1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,

2,从白色的点向汇点连一条边,权值为该白色点的权值,

3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。

最后求最小割(最大流),即为最小点权覆盖集。

因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。 

自写的代码,实现效率和第一种其实是差不多的也是0MS 用Isap做的

 

#include<iostream> 
#include<cstdio> 
#include<memory.h> 
#include<cmath> 
using namespace std;   
#define MAXN 5010 
#define MAXE 1000100 
#define INF 0x3fffffff   
int ne,nv,tmp,s,t,index;  
struct Edge{ 
    int next,pair,v; 
    int cap,fLow; 
}edge[MAXE]; 
int net[MAXN]; 
int ISAP() 
{ 
    int numb[MAXN],dist[MAXN],curedge[MAXN],pre[MAXN]; 
    int cur_fLow,max_fLow;
	int u,tmp,neck,i; 
    memset(dist,0,sizeof(dist)); 
    memset(numb,0,sizeof(numb)); 
    memset(pre,-1,sizeof(pre)); 
    for(i = 1 ; i <= nv ; ++i) 
        curedge[i] = net[i]; 
    numb[nv] = nv; 
    max_fLow = 0; 
    u = s; 
    while(dist[s] < nv) 
    { 
        if(u == t) 
        { 
            cur_fLow = INF+1; 
            for(i = s; i != t;i = edge[curedge[i]].v)  
            {   
                if(cur_fLow > edge[curedge[i]].cap) 
                { 
                    neck = i; 
                    cur_fLow = edge[curedge[i]].cap; 
                } 
            } 
            for(i = s; i != t; i = edge[curedge[i]].v) 
            { 
                tmp = curedge[i]; 
                edge[tmp].cap -= cur_fLow; 
                edge[tmp].fLow += cur_fLow; 
                tmp = edge[tmp].pair; 
                edge[tmp].cap += cur_fLow; 
                edge[tmp].fLow -= cur_fLow; 
            } 
            max_fLow += cur_fLow; 
            u = neck; 
        } 
       for(i = curedge[u]; i != -1; i = edge[i].next) 
            if(edge[i].cap > 0 && dist[u] == dist[edge[i].v]+1) 
                break; 
        if(i != -1) 
        { 
            curedge[u] = i; 
            pre[edge[i].v] = u; 
            u = edge[i].v; 
        }else{ 
            if(0 == --numb[dist[u]]) break; 
            curedge[u] = net[u]; 
            for(tmp = nv,i = net[u]; i != -1; i = edge[i].next) 
                if(edge[i].cap > 0) 
                    tmp = tmp<dist[edge[i].v]?tmp:dist[edge[i].v]; 
            dist[u] = tmp + 1; 
            ++numb[dist[u]]; 
            if(u != s) u = pre[u]; 
        } 
    }      
    return max_fLow; 
}
void addedge(int u,int v,int f)
{
    edge[index].next = net[u]; 
    edge[index].v = v; 
    edge[index].cap = f; 
    edge[index].fLow = 0; 
    edge[index].pair = index+1; 
    net[u] = index++; 
    edge[index].next = net[v]; 
    edge[index].v = u; 
    edge[index].cap = 0; 
    edge[index].fLow = 0; 
    edge[index].pair = index-1; 
    net[v] = index++;
    
} 
int main() { 
    int i,j,m,n;     
    int sum;
    while(scanf("%d%d",&n,&m)!=EOF) 
    { 
		int c;
		sum=0;
        index=0;
        s = 0; 
        t = 2*n*m+1; 
		nv=t+1;
        memset(net,-1,sizeof(net));        
        
        for(i=1;i<=n;i++)
			for(j=1;j<=m;j++) 
        {            
            scanf("%d",&c);
            sum+=c;
            addedge(s,(i-1)*m+j,c);//建立每个点到源点的 
            addedge((i-1)*m+j+n*m,t,c);//建立每个点的次点到汇点的 
             
			if(i>1 && i<=n && j<=m)  //建立与该点上面的 
                addedge((i-1)*m+j,(i-2)*m+n*m+j,INF);  
            if(j>1 && i<=n && j<=m)  //建立与该点左边的 
                addedge((i-1)*m+j, (i-1)*m+j-1+n*m,INF);  
            if(i<n && i>=1 && j<=m)  //建立与该点下面的 
                addedge((i-1)*m+j,i*m+n*m+j,INF);  
            if(j<m && j>=1 && i<=n)  //建立与该点右边的 
                addedge((i-1)*m+j,(i-1)*m+j+n*m+1,INF);  

         }         
        int ans=ISAP();
        printf("%d\n",sum-ans/2);//因为拆点了所以流量要除以2! 
    } 
    return 0; 
}

 

网上找的两种实现代码,注释的是第一种。 

<SPAN style="FONT-FAMILY: SimHei; FONT-SIZE: 16px">/*#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int inf = 0x3fffffff;
const int N = 2600;
const int E = 30000;

int e,head[N];
int dep[N],que[N],cur[N];
struct node
{
    int x,y;
    int nxt;
    int c;
}edge[E];
void addedge(int u,int v,int c)
{
    edge[e].x=u;
    edge[e].y=v;
    edge[e].nxt=head[u];
    edge[e].c=c;
    head[u]=e++;

    edge[e].x=v;
    edge[e].y=u;
    edge[e].nxt=head[v];
    edge[e].c=0;
    head[v]=e++;
}

int maxflow(int s,int t)
{
    int i,j,k,front,rear,top,min,res=0;
    while(1)
    {
        memset(dep,-1,sizeof(dep));
        front=0;
        rear=0;
        que[rear++]=s;
        dep[s]=0;
        while(front!=rear)
        {
            i=que[front++];
            for(j=head[i];j!=-1;j=edge[j].nxt)
                if(edge[j].c&&dep[edge[j].y]==-1)
                {
                    dep[edge[j].y]=dep[i]+1;
                    que[rear++]=edge[j].y;
                
                }
        }
        if(dep[t]==-1)
            break;
        memcpy(cur,head,sizeof(head)); 
        for(i=s,top=0;;)
        {
            if(i==t)
            {
                min=inf;
                for(k=0;k<top;k++)
                    if(min>edge[que[k]].c)
                    {
                        min=edge[que[k]].c;
                        front=k;
                    }
                for(k=0;k<top;k++)
                {
                    edge[que[k]].c-=min;
                    edge[que[k]^1].c+=min;
                }
                res+=min;
                i=edge[que[top=front]].x;
                
            }
            for(j=cur[i];cur[i]!=-1;j=cur[i]=edge[cur[i]].nxt)
                if(dep[edge[j].y]==dep[i]+1&&edge[j].c)
                    break;
            if(cur[i]!=-1)
            {
                que[top++]=cur[i];
                i=edge[cur[i]].y;
            }
            else
            {
                if(top==0)
                    break;
                dep[i]=-1;
                i=edge[que[--top]].x;
            }
        }
    }
    return res;
}
int main()
{
    int n,m,i,j,c,sum;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,-1,sizeof(head));
        e = 0;
        int src = 0;
        int sin = n*m+1;
        sum = 0;
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            {
                scanf("%d",&c);
                sum+=c;
                if((i+j)%2 == 0)
                {    
                addedge(src,(i-1)*m+j,c);            
                if(i>1)
                    addedge((i-1)*m+j,(i-2)*m+j,inf);
                if(j>1)
                    addedge((i-1)*m+j,(i-1)*m+j-1,inf);
                if(i<n)
                    addedge((i-1)*m+j,i*m+j,inf);
                if(j<m) 
                     addedge((i-1)*m+j,(i-1)*m+j+1,inf);
                }
                else addedge((i-1)*m+j,sin,c);
            
            }
    //    cout << sum <<endl;
        printf("%d\n",sum-maxflow(src,sin));
    }
    return 0;
}
*/


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N = 5100;
const int E = 80000;
const int inf = 0x3fffffff;

int e,head[N];
int dep[N],que[N],cur[N];

struct node
{
    int x,y;
    int nxt;
    int c;
}edge[E];
void addedge(int u,int v,int c)
{
    edge[e].x=u;
    edge[e].y=v;
    edge[e].nxt=head[u];
    edge[e].c=c;
    head[u]=e++;

    edge[e].x=v;
    edge[e].y=u;
    edge[e].nxt=head[v];
    edge[e].c=0;
    head[v]=e++;
}

int maxflow(int s,int t)
{
    int i,j,k,front,rear,top, min,res=0;
    while(1)
    {
        memset(dep,-1,sizeof(dep));
        front=0;
        rear=0;
        que[rear++]=s;
        dep[s]=0;
        while(front!=rear)
        {
            i=que[front++];
            for(j=head[i];j!=-1;j=edge[j].nxt)
                if(edge[j].c&&dep[edge[j].y]==-1)
                {
                    dep[edge[j].y]=dep[i]+1;
                    que[rear++]=edge[j].y;
                
                }
        }
        if(dep[t]==-1)
            break;
        memcpy(cur,head,sizeof(head)); 
        for(i=s,top=0;;)
        {
            if(i==t)
            {
                min=inf;
                for(k=0;k<top;k++)
                    if(min>edge[que[k]].c)
                    {
                        min=edge[que[k]].c;
                        front=k;
                    }
                for(k=0;k<top;k++)
                {
                    edge[que[k]].c-=min;
                    edge[que[k]^1].c+=min;
                }
                res+=min;
                i=edge[que[top=front]].x;
                
            }
            for(j=cur[i];cur[i]!=-1;j=cur[i]=edge[cur[i]].nxt)
                if(dep[edge[j].y]==dep[i]+1&&edge[j].c)
                    break;
            if(cur[i]!=-1)
            {
                que[top++]=cur[i];
                i=edge[cur[i]].y;
            }
            else
            {
                if(top==0)
                    break;
                dep[i]=-1;
                i=edge[que[--top]].x;
            }
        }
    }
    return res;
}
int main ()
{
    int n, m,i,j;
    int sum, c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
          sum = 0;
          int src = 0;
          int sin = 2*n*m+1;
          e =0;
          memset(head,-1,sizeof(head));
          for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            {
                scanf("%d",&c);
                sum += c;
                addedge(src,(i-1)*m+j,c);//建立源点到每个点的
                addedge((i-1)*m+j+n*m,sin,c);
                if(i>1)
                    addedge((i-1)*m+j,(i-2)*m+n*m+j,inf);
                if(j>1)
                    addedge((i-1)*m+j, (i-1)*m+j-1+n*m,inf);
                if(i<n)
                    addedge((i-1)*m+j,i*m+n*m+j,inf);
                if(j<m)
                    addedge((i-1)*m+j,(i-1)*m+j+n*m+1,inf);
            }
            int flow = maxflow(src,sin);
            printf("%d\n",sum-flow/2);
    }
    return 0;
}
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值