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

 

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1569

 

题解:
因为这个数据比较大,所以用动态规划会超时。
将图转换成黑白棋盘问题,i + j 为奇数的与s节点相连,边的权值为棋盘上对应位置的值,其他的与t节点相连,

边的权值为棋盘上对应位置的值,然后让棋盘上相邻之间的节点用边相连,边的权值为INF。这样问题就转换为

了最大点权独立集问题。
定理:
1、最大点权独立集 = sum - 最小点权覆盖集。
2、最小点权覆盖集 = 最小割 = 最大流

dinic实现:

 

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2550;
const int inf = 0x7fffffff;

struct node
{
    int v,cost;
    int next;
}edge[8*maxn];
int map[55][55];
int head[maxn],dis[maxn],id;
int source,sink,res;
int n,m;
void add_edge(int u,int v,int cost)
{
    edge[id].v = v;edge[id].cost = cost;edge[id].next = head[u];head[u] = id++;
    edge[id].v = u;edge[id].cost = 0;   edge[id].next = head[v];head[v] = id++;
}
void build()
{
    id = source = 0;sink = n*m+5;
    memset(head,-1,sizeof(head));
    int i,j,t;
    for( i = 1; i <= n; i++)
    {
        for( j = 1; j <= m; j++)
        {
            t = (i-1)*m+j+1;
            if((i+j)&1)
            {
                add_edge(source,t,map[i][j]);
                if( i > 1)
                    add_edge(t,t-m,inf);
                if( i < n )
                    add_edge(t,t+m,inf);
                if( j > 1)
                    add_edge(t,t-1,inf);
                if( j < m )
                    add_edge(t,t+1,inf);
            }
            else
                add_edge(t,sink,map[i][j]);
        }
    }
}
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue<int>que;
    dis[source] = 0;
    que.push(source);

    while( !que.empty())
    {
        int u = que.front();
        que.pop();

        for( id = head[u] ; id != -1; id = edge[id].next)
        {
            int v = edge[id].v;
            if( edge[id].cost > 0 && dis[v] == -1)
            {
                dis[v] = dis[u] + 1;
                que.push(v);
            }
        }
    }
    return dis[sink] != -1;
}

int dinic(int u,int flow)
{
    if( u == sink)return flow;
    int tmp = flow;
    for(int id = head[u] ; id != -1 ; id = edge[id].next)
    {
        int v = edge[id].v;
        if( edge[id].cost > 0 && dis[v] == dis[u] + 1)
        {
            int c = dinic(v,edge[id].cost < tmp ? edge[id].cost : tmp);
            tmp -= c;
            edge[id].cost -= c;
            edge[id^1].cost += c;
            if( tmp == 0)break;
        }
    }
    return flow - tmp;
}

int get_max_flow()
{
    int max_flow = 0;
    while( bfs() )
        max_flow += dinic(source,res);
    return max_flow;
}


int main()
{
    int i,j;
    while( scanf("%d%d",&n,&m) != EOF)
    {
        res = 0;
        for( i = 1; i <= n; i++)
        for( j = 1; j <= m; j++)
        {
            scanf("%d",&map[i][j]);
            res += map[i][j];
        }
        build();
        printf("%d\n",res-get_max_flow());
    }
    return 0;
}


 

转载于:https://www.cnblogs.com/LUO257316/archive/2013/05/29/3220803.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值