HDU 1565 方格取数(1) 网络流的应用

中文题意不解释。。

要懂这道题,要先懂得一些基本的概念。。在我的博客里面也有这些概念。。我这里再重复一下,也说明这些概念的重要性。。

HDU 1565 网络流与方格取数问题

基于二分图性质的一些推论,可利用最大流+最小切割解决一类网络流问题: HDU 1565 方格取数(1)

题目大意:

给定一个尺寸的方格,每个格子拥有一个非负数,求一种满足题目约束的取数方法,最重使得取数总和最大。约束规则为,任意两个被取的格子间不能拥有公共边,也就是不能相邻。

那么这个问题怎么解决呢?很多童鞋做网络流的时候,一旦没有思路就上网搜解题报告,个人觉得你网路流的构图都是别人告诉你了,这题基本就作废了~

先来回顾以下几个推论:

点覆盖集:无向图G的一个点集,使得该图中所有边都至少有一个端点在该点集中。

最小点权覆盖集:在带点权无向图G中,点权和最小的点覆盖集。

点独立集:无向图G的一个点集,使得任何两个在点集中的点在图G中都不相邻。

最大点权独立集:在无向带权图G中,点权和最大的点独立集。

最小点权覆盖集=最小割=最大流

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

——————>    胡伯涛《最小割模型在信息学竞赛中的应用》

于是回到这个问题中,我们可以看到,不存在任意两个被取方格存在公共边,如果将方格抽象成顶点,相邻关系抽象成边,那么约束就转变成了相邻顶点不能同时被选取,原问题转化成为求一个图上的顶点独立集。由于相邻顶点黑白染色之后,方格可被转化为二分图,在二分图上求顶点独立集就简单了。由最大点独立集=全集-最小点覆盖集关系,问题变成求解二分图的最小顶点覆盖,最后覆盖集的补集为所求。这就是这个问题可由网络流解决的论证思路。其实由于网络流完成后最小割集合对应不取集合的最小解,那么自然也就使得可取的集合解最大了。

构图方法 方格顶点黑白染色,黑点向相邻白点连有向弧,容量正无穷;所有黑点被源点连,容量对用方格数;所有白点连向汇点,

容量方格数

上面的这些转自:http://isomer.me/2011/04/hdu-1565-%E7%BD%91%E7%BB%9C%E6%B5%81%E4%B8%8E%E6%96%B9%E6%A0%BC%E5%8F%96%E6%95%B0%E9%97%AE%E9%A2%98/

 

然后就可以根据上面的方法来做了。

//这道题用EK会TLE,,要用ISPA,DINIC。。。
//还有就是要学会黑白染色的两种方法。。
//还有就是模板的灵活运用。。

我下面给出两种染色的方法:

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define inf 0x7FFFFFFF
const int N=410; //这里比较坑爹,,要开大一点。。
const int INF = 0x7FFFFFFF;
int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int n,s,t,dis[N],pre[N],gap[N],flow[N][N]; 

struct edge 

    int v,w; 
    edge *next,*rev; 
    edge(){next=0;} 
    edge(int vv,int ww,edge *e)
 {v=vv;w=ww;next=e;} 
}*adj[N],*path[N],*e; 

int min(int x,int y)
{
 if(x>y)
  return y;
 else
  return x;
}
void insert(int u,int v,int w) 

    edge *p=new edge(v,w,adj[u]); 
    adj[u]=p; 
    edge *q=new edge(u,0,adj[v]); 
    adj[v]=q; 
    p->rev=q; 
    q->rev=p; 

void bfs() 

    memset(dis,0x7f,sizeof(dis)); 
    memset(gap,0,sizeof(gap)); 
    queue<int> q; 
    dis[t]=0; 
    gap[0]=1; 
    q.push(t); 
    while(q.size()) 
    { 
        int x=q.front(); 
        q.pop(); 
        for(e=adj[x];e;e=e->next) 
        { 
            if(e->rev->w==0||dis[e->v]<t) 
                continue; 
            dis[e->v]=dis[x]+1; 
            ++gap[dis[e->v]]; 
            q.push(e->v); 
        } 
    } 

int ISAP() 

    memset(dis,0,sizeof(dis)); 
    memset(gap,0,sizeof(gap)); 
    //bfs(); 
    int ans=0,u=s,d;
    while(dis[s]<=t) 
    { 
        if(u==t) 
        { 
            int minflow=-1u>>1; 
            for(e=path[u];u!=s;e=path[u=pre[u]]) 
                minflow=min(minflow,e->w); 
            for(e=path[u=t];u!=s;e=path[u=pre[u]]) 
            { 
                e->w-=minflow; 
                e->rev->w+=minflow; 
                flow[pre[u]][u]+=minflow; 
                flow[u][pre[u]]-=minflow; 
            } 
            ans+=minflow; 
        }        
        for(e=adj[u];e;e=e->next) 
            if(e->w>0&&dis[u]==dis[e->v]+1) 
                break; 
   if(e) 
   { 
    pre[e->v]=u; 
    path[e->v]=e; 
    u=e->v; 
   } 
   else 
   { 
    if(--gap[dis[u]]==0) 
     break; 
    for(d=t,e=adj[u];e;e=e->next) 
     if(e->w>0) 
      d=min(d,dis[e->v]); 
     dis[u]=d+1; 
     ++gap[dis[u]]; 
     if(u!=s) 
      u=pre[u]; 
   } 
    } 
    return ans; 
}
int main()
{
 int i,j;
 int sum;
 int temp;
 while(scanf("%d",&n)!=EOF)
 {
  sum=0;
  memset(adj,0,sizeof(adj));//这句要记得加哦。。
  for(i=1;i<=n;i++)
   for(j=1;j<=n;j++)
   {
    scanf("%d",&temp);
                if ((i+j)%2==0)
                    insert(0,(i-1)*n+j,temp);//坐标和为偶数的点和源点0相连
                else
                    insert((i-1)*n+j,n*n+1,temp);//坐标和为奇数数的点和汇点n*n+1相连
                sum+=temp;
   } 
   for(i=1;i<=n;i++)
    for(j=1;j<=n;j++)
     if((i+j)%2==0)
     {
      for(int k=0;k<4;k++)
      {
       int a=i+move[k][0];
       int b=j+move[k][1];
       if (a>=1&&a<=n&&b>=1&&b<=n)
       {
        insert((i-1)*n+j,(a-1)*n+b,INF);
       }
      }
     }
     s=0,t=n*n+1;
     printf("%d\n",sum-ISAP());
     
 } 
 return 0;
}

 

/*
下面是另一种染色方法:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define inf 0x7FFFFFFF
const int N=410;
const int INF = 0x7FFFFFFF;
int move[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int n,s,t,dis[N],pre[N],gap[N],flow[N][N]; 
int res[N][N],cor[N][N];

struct edge 

    int v,w; 
    edge *next,*rev; 
    edge(){next=0;} 
    edge(int vv,int ww,edge *e)
 {v=vv;w=ww;next=e;} 
}*adj[N],*path[N],*e; 

int min(int x,int y)
{
 if(x>y)
  return y;
 else
  return x;
}
void insert(int u,int v,int w) 

    edge *p=new edge(v,w,adj[u]); 
    adj[u]=p; 
    edge *q=new edge(u,0,adj[v]); 
    adj[v]=q; 
    p->rev=q; 
    q->rev=p; 

void bfs() 

    memset(dis,0x7f,sizeof(dis)); 
    memset(gap,0,sizeof(gap)); 
    queue<int> q; 
    dis[t]=0; 
    gap[0]=1; 
    q.push(t); 
    while(q.size()) 
    { 
        int x=q.front(); 
        q.pop(); 
        for(e=adj[x];e;e=e->next) 
        { 
            if(e->rev->w==0||dis[e->v]<t) 
                continue; 
            dis[e->v]=dis[x]+1; 
            ++gap[dis[e->v]]; 
            q.push(e->v); 
        } 
    } 

int ISAP() 

    memset(dis,0,sizeof(dis)); 
    memset(gap,0,sizeof(gap)); 
    //bfs(); 
    int ans=0,u=s,d;
    while(dis[s]<=t) 
    { 
        if(u==t) 
        { 
            int minflow=-1u>>1; 
            for(e=path[u];u!=s;e=path[u=pre[u]]) 
                minflow=min(minflow,e->w); 
            for(e=path[u=t];u!=s;e=path[u=pre[u]]) 
            { 
                e->w-=minflow; 
                e->rev->w+=minflow; 
                flow[pre[u]][u]+=minflow; 
                flow[u][pre[u]]-=minflow; 
            } 
            ans+=minflow; 
        }        
        for(e=adj[u];e;e=e->next) 
            if(e->w>0&&dis[u]==dis[e->v]+1) 
                break; 
   if(e) 
   { 
    pre[e->v]=u; 
    path[e->v]=e; 
    u=e->v; 
   } 
   else 
   { 
    if(--gap[dis[u]]==0) 
     break; 
    for(d=t,e=adj[u];e;e=e->next) 
     if(e->w>0) 
      d=min(d,dis[e->v]); 
     dis[u]=d+1; 
     ++gap[dis[u]]; 
     if(u!=s) 
      u=pre[u]; 
   } 
    } 
    return ans; 
}
int main()
{
 int i,j;
 int sum;
 int temp;
 while(scanf("%d",&n)!=EOF)
 {
  sum=0;
  memset(adj,0,sizeof(adj));//这句要记得加哦。。
  for(i=1;i<=n;i++)
   for(j=1;j<=n;j++)
   {
    cin>>res[i][j];
    sum+=res[i][j];
   }
   for(i=1;i<=n;i+=2)
    cor[1][i]=1;
   for(i=2;i<=n;i++)
    for(j=1;j<=n;j++)
     cor[i][j]=!cor[i-1][j];
    
    for(i=1;i<=n;i++)
     for(j=1;j<=n;j++)
     {
      if(cor[i][j]==0)
      {
       insert(0,(i-1)*n+j,res[i][j]);
      }
      else
       if(cor[i][j]==1)
       {
        insert((i-1)*n+j,n*n+1,res[i][j]);
       }
     }
     
     for(i=1;i<=n;i++)
      for(j=1;j<=n;j++)
       if(cor[i][j]==0)
       {
        for(int k=0;k<4;k++)
        {
         int a=i+move[k][0];
         int b=j+move[k][1];
         if(a>=1&&a<=n&&b>=1&&b<=n)
         {
          insert((i-1)*n+j,(a-1)*n+b,INF);
         }
        }
       }
       s=0,t=n*n+1;
       printf("%d\n",sum-ISAP());
       
 } 
 return 0;
}*/

 

转载于:https://www.cnblogs.com/Mu-Tou/archive/2011/08/16/2140151.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值