hdu 1565 方格取数(1)(最小割--最大权独立点集)

本文详细介绍了如何使用最大流算法求解最大权独立点集问题,通过构建特殊的二分图并应用最小割概念,实现对给定图中点权的最大化选择。通过实例分析,深入理解该算法的核心思想及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

点击打开链接

题目大意:

给出一个方格,每个格存在一个具有权值的点,去除不相邻的一些数,使得出的权值和最大

题目分析:

我们把相邻当做一种关系建边,然后横纵坐标相加之和是奇数的点和横纵之和相加是偶数的点一定不会存在边,所以这个图就变成了一个二分图,那么问题也就抽象为了求最大权独立点集,最大权独立点集因为和最小权覆盖集互为补集,所以我们可以先求出最小权覆盖集,首先我们考虑建图的方法:

1.根据划分出的二分图,左集中的点与源点相连,边权就等于点权,右集的点与汇点相连,边权就等于点权,原图中的边将左集中的点连向右集,边权定为INF,那么接下来我们考虑为何要这么建图

2.根据最小割的定义,我们求取最大流也就是最小割,是将当前图分为S集合T集,切掉的边流量之和最小的,也就是去掉点权之和最少的点,导致所有相连的点都不能从原点到达汇点,也就是切掉其中一条点权边,那么就相当于将这个点放入了最小权点覆盖集,那么最大权点独立集,直接利用总点权减去最小割即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#define MAX 507
#define INF (1<<29)

using namespace std;

struct Edge
{
    int u,v,flow,next;
}e[MAX*MAX];

int head[MAX];
int cc;

void init ( )
{
    memset ( head, -1 , sizeof ( head ));
    cc = 0;
}

void add ( int u , int v , int c )
{
    e[cc].u = u;
    e[cc].v = v;
    e[cc].flow = c;
    e[cc].next = head[u];
    head[u] = cc++;
    e[cc].u = v;
    e[cc].v = u;
    e[cc].flow = 0;
    e[cc].next = head[v];
    head[v] = cc++;
}

int S,T,n;
int d[MAX];

bool bfs ( )
{
    int q[MAX],iq=0;
    memset ( d , 0 , sizeof ( d ));
    d[S] = 1;
    q[iq++] = S;
    for ( int i = 0 ; i < iq ; i++ )
    {
        int u = q[i];
        if ( u == T ) return true;
        for ( int j = head[u]; j != -1 ; j = e[j].next )
        {
            int v = e[j].v;
            if ( !d[v] && e[j].flow )
            {
                q[iq++] = v;
                d[v] = d[u]+1;
            }
        }
    }
    return false;
}

int dfs ( int u , int cur_flow )
{
    if ( u == T ) return cur_flow;
    int ret = 0;
    for ( int i = head[u] ; i != -1 ; i = e[i].next )
    {
        int v = e[i].v;
        if ( e[i].flow&& d[u]+1 == d[v] )
        {
            int flow = dfs ( v , min ( cur_flow - ret, e[i].flow ));
            e[i].flow -= flow;
            e[i^1].flow += flow;
            ret += flow;
            if ( ret == cur_flow ) return ret;
        }
    }
    return ret;
}

int dinic ( )
{
    int cur_flow, ans = 0;
    while ( bfs ())
    {
        while ( cur_flow = dfs ( S , INF))
            ans += cur_flow;
    }
    return ans;
}

int mp[27][27];
int id[27][27];
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};

bool check ( int x ,int y )
{
    if ( x < 0 || x >= n || y < 0 || y >= n )
        return true;
    return false;
}

int main ( )
{
    while ( ~scanf ( "%d" , &n ))
    {
        int sum = 0;
        for ( int i = 0 ; i < n ; i++ )
            for ( int j = 0 ; j < n ; j++ )
            {
                scanf ( "%d" , &mp[i][j] );
                sum += mp[i][j];
            }

        int cnt = 1;
        for ( int i = 0 ; i < n ; i++ )
            for ( int j = 0 ; j < n ; j++ )
            id[i][j] = cnt++;
        S = 0 , T = cnt;

        init();
        for ( int i = 0 ; i < n ; i++ )
            for ( int j = 0 ; j < n ; j++ )
            {
                if ( (i+j)%2 == 1 )
                {
                    add ( S , id[i][j] , mp[i][j] );
                    for ( int k = 0 ; k < 4 ; k++ )
                    {
                        int x = i + dx[k];
                        int y = j + dy[k];
                        if (check ( x , y )) continue;
                        add ( id[i][j] , id[x][y] , INF );
                    }
                }
                else
                    add ( id[i][j] , T , mp[i][j] );

            }
        printf ( "%d\n" , sum - dinic());

    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值