【网络流第四弹】最大点权独立集 ——HDU 1565 方格取数(1)

题目:点击打开链接

网络流学习的下一步,最大点权独立集。多和最小点权覆盖集放到一起使用。分别求图中不相邻的点权最大/或者边中至少有一点在集合中,求最小总权值的问题。

公式:

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

最小点权覆盖集=图的最小割值=最大流。和普通网络流一样,可以使用ISAP,EK,DINIC等多种方法。

建图:

多采用奇偶建立二分图的方法。建边时脑子一定要清楚。相邻点要染不同的颜色,源点为0,汇点为最后的那个点。染色方法基本上是判断if(边+边)%2==0源点与其连接,反之汇点与其连接。如果两点相邻的话,连接值为INF,无穷大。

脑子一定要清醒。。wa了若干次,用的是最快的sap.

#include <iostream>
#include <cstring>
#include <string>
#include <iomanip>
using namespace std;

typedef  struct {int v,next,val;} edge;
const int INF=0x3F3F3F3F;
 const int MAXN=2000;
 const int MAXM=50000;
 edge e[MAXM];
 int p[MAXN],eid;
 inline void init(){memset(p,-1,sizeof(p));eid=0;}
 
 //有向
 inline void insert1(int from,int to,int val)
 {
     e[eid].v=to;
     e[eid].val=val;
     e[eid].next=p[from];
     p[from]=eid++;
     swap(from,to);
     e[eid].v=to;
     e[eid].val=0;
     e[eid].next=p[from];
     p[from]=eid++;
 }
 
 //无向
 inline void insert2(int from,int to,int val)
 {
     e[eid].v=to;
     e[eid].val=val;
     e[eid].next=p[from];
     p[from]=eid++;
     swap(from,to);
     e[eid].v=to;
     e[eid].val=val;
     e[eid].next=p[from];
     p[from]=eid++;
 }
 int n,m;//n为点数 m为边数
 int h[MAXN];
 int gap[MAXN];
 int source,sink;
 inline int dfs(int pos,int cost)
 {
     if (pos==sink)
     {
         return cost;
     }
     int j,minh=n-1,lv=cost,d;
     for (j=p[pos];j!=-1;j=e[j].next)
     {
         int v=e[j].v,val=e[j].val;
         if(val>0)
         {
             if (h[v]+1==h[pos])
             {
                 if (lv<e[j].val) d=lv;
                 else d=e[j].val;
                 
                 d=dfs(v,d);
                 e[j].val-=d;
                 e[j^1].val+=d;
                 lv-=d;
                 if (h[source]>=n) return cost-lv;
                 if (lv==0) break;
             }
             if (h[v]<minh)    minh=h[v];
         }
     }
     if (lv==cost)
     {
         --gap[h[pos]];
         if (gap[h[pos]]==0) h[source]=n;
         h[pos]=minh+1;
         ++gap[h[pos]];
     }
     return cost-lv;
 }
 int sap(int st,int ed)
 {
     source=st;
     sink=ed;
     int ret=0;
     memset(gap,0,sizeof(gap));
     memset(h,0,sizeof(h));
     gap[st]=n;
     while (h[st]<n)
     {
         ret+=dfs(st,INF);
     }
     return ret;
 }
 int main()
 {
      int pt,ma;
     while(cin>>pt)
     {
         int tmp;
          init();
          n=2000;
           m=10000;
           int sum=0; //最大点权独立集=总权值-最小点权覆盖集=总权值-最小割
          int st=0;  //start point
          int ed=(pt*pt)+1;
          for(int i=1;i<=pt;i++)
          {
              for(int j=1;j<=pt;j++)
              {
                  scanf("%d",&tmp);
                  sum+=tmp;
                  if((i+j)%2==0)  //偶数序号点
                {
                    insert1(st,(i-1)*pt+j,tmp);
                    if(i>1)
                    insert1((i-1)*pt+j,(i-2)*pt+j,INF);
                    if(j>1)
                    insert1((i-1)*pt+j,(i-1)*pt+j-1,INF);  //顺序一定不能错 
                    if(i<pt)
                    insert1((i-1)*pt+j,(i)*pt+j,INF);
                    if(j<pt)
                    insert1((i-1)*pt+j,(i-1)*pt+j+1,INF);
                }
                else
                    insert1((i-1)*pt+j,ed,tmp);
                  
            }
            
          }
          int res=sap(st,ed);
          cout<<sum-res<<endl;
      } 
     return 0;
 }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值