hdu 1569 &1565 二分图带权最大独立集,最大流最小割定理

此题可以通过奇偶建立二分图,将奇数点集令为X集,偶数点集令为Y集。

二分图带权最大独立集:给出一个二分图,每个结点上有一个正权值。要求选出一些点,使得这些点之间没有边相连,且权值最大。(和题目所要求的一样)

所以我们可以将X集中与Y集中相邻的点连一条边,这样就构成了一个二分图。

用<<训练指南上>>P367页 的建模方法

添加一个源点和X集相连,边容量为点权。

添加一个汇点与Y集相连,边容量为点权。

将X-Y原来的边的容量设为无穷大。

求出最小割,剩下的就是没有边相连的最大独立集,也就是答案。

而最大流=最小割。

dinic实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 2600
#define INF 0x3f3f3f3f
#define isok(x,y) (x>=1&&x<=n&&y>=1&&y<=m)
struct edge
{
	int to,c,next;
};
edge e[999999];
int que[MAXN*100];
int dis[MAXN];
int pre[MAXN];
int head[MAXN],head2[MAXN];
int st,ed;
int maxflow;
int en;
int n,m;
int id;
int mp[55][55];
void add(int a,int b,int c)
{
	e[en].to=b;
	e[en].c=c;
	e[en].next=head[a];
	head[a]=en++;
	e[en].to=a;
	e[en].c=0;
	e[en].next=head[b];
	head[b]=en++;
}
bool bfs()
{
	memset(dis,-1,sizeof(dis));
	que[0]=st,dis[st]=1;
	int t=1,f=0;
	while(f<t)
	{
		int j=que[f++];
		for(int k=head[j];k!=-1;k=e[k].next)
		{
			int i=e[k].to;
			if(dis[i]==-1&&e[k].c)
			{
				que[t++]=i;
				dis[i]=dis[j]+1;
				if(i==ed) return true;
			}
		}
	}
	return false;
}
int update()
{
	int p,flow=INF;
    for (int i=pre[ed];i!=-1;i=pre[i])
		if(e[head2[i]].c<flow) p=i,flow=e[head2[i]].c;
    for (int i=pre[ed];i!=-1;i=pre[i])
		e[head2[i]].c-=flow,e[head2[i]^1].c+=flow;
    maxflow+=flow;
    return p;
}
void dfs()
{
	memset(pre,-1,sizeof(pre));
	memcpy(head2,head,sizeof(head2));
    for(int i=st,j;i!=-1;)
    {
        int flag=false;
        for(int k=head[i];k!=-1;k=e[k].next)
          if(e[k].c && (dis[j=e[k].to]==dis[i]+1) )
          {
                pre[j]=i;
				head2[i]=k;
				i=j;
				flag=true;
                if(i==ed)
					i=update();
                if(flag)
					break;
          }
        if (!flag) dis[i]=-1,i=pre[i];
    }
}
int dinic()
{
	maxflow=0;
	while(bfs())
		dfs();
	return maxflow;
}
void init()
{
	int a,b,c;
	en=0;
	st=n*m+1;     //源
    ed=n*m+2;     //汇
	memset(head,-1,sizeof(head));
}
void input()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&mp[i][j]);
        }
    }
}
long long sum;
void build()
{
    id=1;
    int dx[]={-1,1,0,0};
    int dy[]={0,0,-1,1};
    sum=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++,id++)
        {
            sum+=mp[i][j];
            if((i+j)&1)
            {
                add(st,id,mp[i][j]);
                for(int d=0;d<4;d++)
                {
                    if(!isok(i+dx[d],j+dy[d])) continue;
                    int nid=id+dx[d]*m+dy[d];
                    add(id,nid,INF);
                }
            }
            else add(id,ed,mp[i][j]);
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        input();
        build();
        cout<<sum-dinic()<<endl;
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TommyTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值