关闭

hdu 3657(最小割求解最大点权独立集)

标签: 网络流
198人阅读 评论(0) 收藏 举报
分类:

Game

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
onmylove has invented a game on n × m grids. There is one positive integer on each grid. Now you can take the numbers from the grids to make your final score as high as possible. The way to get score is like
the following:
● At the beginning, the score is 0;
● If you take a number which equals to x, the score increase x;
● If there appears two neighboring empty grids after you taken the number, then the score should be decreased by 2(x&y). Here x and y are the values used to existed on these two grids. Please pay attention that "neighboring grids" means there exits and only exits one common border between these two grids.

Since onmylove thinks this problem is too easy, he adds one more rule:
● Before you start the game, you are given some positions and the numbers on these positions must be taken away.
Can you help onmylove to calculate: what's the highest score onmylove can get in the game?
 

Input
Multiple input cases. For each case, there are three integers n, m, k in a line.
n and m describing the size of the grids is n ×m. k means there are k positions of which you must take their numbers. Then following n lines, each contains m numbers, representing the numbers on the n×m grids.Then k lines follow. Each line contains two integers, representing the row and column of one position
and you must take the number on this position. Also, the rows and columns are counted start from 1.
Limits: 1 ≤ n, m ≤ 50, 0 ≤ k ≤ n × m, the integer in every gird is not more than 1000.
 

Output
For each test case, output the highest score on one line.
 

Sample Input
2 2 1 2 2 2 2 1 1 2 2 1 2 7 4 1 1 1
 

Sample Output
4 9
Hint
As to the second case in Sample Input, onmylove gan get the highest score when calulating like this: 2 + 7 + 4 - 2 × (2&4) - 2 × (2&7) = 13 - 2 × 0 - 2 × 2 = 9.
 

解题思路:这题和之前的方格取数很像,但略微不同,这里有一些限制。基本的思路还是sum - 未取方格的最小值,即sum - 最小割。
关键还是建图:

一个超级原点,和左边的点相连接,容量是其权值。

一个超级汇点,右边的点和其连接,容量是其权值。

如果左边的点x和右边的点y相邻,连接x,y容量为2 * (x & y)

这样的图的最小割的定义是:

一个完整的取数过程中的最小花费。

左边的点x和原点连接表示选x,不连接(切断,割)表示不选x,代价就是x的权值。同理右边的点也是。

如果左边的x和原点连接,右边的y和汇点连接,那么满足割的定义,xy之间如果有边,一定要切断,代价就是2 * (x & y)。

同时,为了满足一定要取一些点的附加条件,只要让那些点和对应的原点或者汇点容量为无穷大就可以了,这样割一定不会割那条无穷大

参考博客:http://blog.sina.com.cn/s/blog_64675f540100l4h2.html

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 55
#define MAXM 55*55
#define inf 1<<30
using namespace std;
 struct Edge{
     int v,cap,next;
 }edge[MAXM*11];

int map[MAXN][MAXN];
int head[MAXM];
int pre[MAXM];
int cur[MAXM];
int level[MAXM];
int gap[MAXM];
int vs,vt,NV,n,m,k,NE;
bool mark[MAXN][MAXN];
int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};

 
 void Insert(int u,int v,int cap,int cc=0){
     edge[NE].cap=cap;edge[NE].v=v;
     edge[NE].next=head[u];head[u]=NE++;
 
     edge[NE].cap=cc;edge[NE].v=u;
     edge[NE].next=head[v];head[v]=NE++;
 }
 
 
 //参数,源点,汇点
 int SAP(int vs,int vt){
     memset(level,0,sizeof(level));
     memset(pre,-1,sizeof(pre));
     memset(gap,0,sizeof(gap));
     //cur[i]保存的是当前弧
     for(int i=0;i<=NV;i++)cur[i]=head[i];
     int u=pre[vs]=vs;//源点的pre还是其本身
     int maxflow=0,aug=-1;
     gap[0]=NV;
     while(level[vs]<NV){
 loop :
         for(int &i=cur[u];i!=-1;i=edge[i].next){
             int v=edge[i].v;//v是u的后继
             //寻找可行弧
             if(edge[i].cap&&level[u]==level[v]+1){
                 //aug表示增广路的可改进量
                 aug==-1?(aug=edge[i].cap):(aug=min(aug,edge[i].cap));
                 pre[v]=u;
                 u=v;
                 //如果找到一条增广路
                 if(v==vt){
                     maxflow+=aug;//更新最大流;
                     //路径回溯更新残留网络
                     for(u=pre[v];v!=vs;v=u,u=pre[u]){
                         //前向弧容量减少,反向弧容量增加
                         edge[cur[u]].cap-=aug;
                         edge[cur[u]^1].cap+=aug;
                     }
                     aug=-1;
                 }
                 goto loop;
             }
         }
         int minlevel=NV;
         //寻找与当前点相连接的点中最小的距离标号(重标号)
         for(int i=head[u];i!=-1;i=edge[i].next){
             int v=edge[i].v;
             if(edge[i].cap&&minlevel>level[v]){
                 cur[u]=i;//保存弧
                 minlevel=level[v];
             }
         }
         if((--gap[level[u]])==0)break;//更新gap数组后如果出现断层,则直接退出。
         level[u]=minlevel+1;//重标号
         gap[level[u]]++;//距离标号为level[u]的点的个数+1;
         u=pre[u];//转当前点的前驱节点继续寻找可行弧
     }
     return maxflow;
 } 


int main(){
    int u,v,sum;
    while(~scanf("%d%d%d",&n,&m,&k)){
        vs=0,vt=n*m+1,NV=vt+1,sum=0,NE=0;
        memset(mark,false,sizeof(mark));
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&map[i][j]);
                sum+=map[i][j];
            }
        }
        
        for(int i=1;i<=k;i++){
            scanf("%d%d",&u,&v);
            mark[u][v]=true;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if((i+j)%2==1){
                    mark[i][j]?Insert(vs,(i-1)*m+j,inf):Insert(vs,(i-1)*m+j,map[i][j]);
                    for(int l=0;l<4;l++){
                        int xx=i+dir[l][0],yy=j+dir[l][1];
                        if(xx>=1&&xx<=n&&yy>=1&&yy<=m){
                            Insert((i-1)*m+j,(xx-1)*m+yy,(map[i][j]&map[xx][yy])*2);
                        }
                    }
                }else{
                    mark[i][j]?Insert((i-1)*m+j,vt,inf):Insert((i-1)*m+j,vt,map[i][j]);
                }
            }
        }
        printf("%d\n",sum-SAP(vs,vt));
    }
    return 0;
}



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:65783次
    • 积分:4504
    • 等级:
    • 排名:第6560名
    • 原创:395篇
    • 转载:54篇
    • 译文:0篇
    • 评论:3条
    最新评论