方格取数3

 方格取数 3

 时间限制: 2 s
 空间限制: 256000 KB
 
 
 
题目描述 Description

在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任
意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

输入描述 Input Description

第1 行有2 个正整数m和n,分别表示棋盘的行数
和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。

输出描述 Output Description

将取数的最大总和输出

样例输入 Sample Input

3 3
1 2 3
3 2 3
2 3 1

样例输出 Sample Output

11

数据范围及提示 Data Size & Hint

n,m<=30


最小割。由于题目要求不能选择相邻的两个数,我们可以考虑给棋盘染色,白格子只与黑格子相邻,反之亦然。源点都和某一种颜色的格子相连,汇点和另一种颜色的格子相连,权值为格子里的数。相邻两个格子之间建一条流量为无穷大的边,这样就保证最小割只会选择去除与源点或汇点相邻的边,而去除这条边就意味着不选这个格子里的数。最后当最小割选择完成后,源点和汇点没有路径相连,这就意味着没有相邻的格子被同时保留。(因为源点和汇点连接必须要经过相邻格子之间的边,而现在源点和汇点不相连,就意味着一旦某一个格子被选择,它周围的格子都不能再被选择了)。

 

#include<bits/stdc++.h>
#define N 1000
using namespace std;

long long tot=0;
typedef struct{long long v,flow;}ss;
vector<long long>edges[N];
ss edg[N*N];
long long dis[N];
long long S,T;
long long current[N];

void addedge(long long u,long long v,long long flow)
{
    edg[tot]=(ss){v,flow};
    edges[u].push_back(tot++);
    edg[tot]=(ss){u,0};
    edges[v].push_back(tot++);
}

bool bfs()
{
    queue<long long>q;
    q.push(S);
    for(long long i=0;i<N;i++)dis[i]=LLONG_MAX/2;
    dis[S]=1;

    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        long long Size=edges[now].size();

        for(long long i=0;i<Size;i++)
        {
            ss &e=edg[edges[now][i]];

            if(e.flow>0&&dis[e.v]==LLONG_MAX/2)
            {
                dis[e.v]=dis[now]+1;
                q.push(e.v);
            }
        }
    }

    if(dis[T]==LLONG_MAX/2)return 0;
    return 1;
}

long long dfs(long long x,long long flow)
{
    if(x==T)return flow;

    long long Size=edges[x].size();
    for(long long i=current[x];i<Size;i++)
    {
        current[x]=i;
        ss &e=edg[edges[x][i]];
        if(dis[x]+1==dis[e.v]&&e.flow>0)
        {
            long long Flow=dfs(e.v,min(flow,(long long)e.flow));
            if(Flow!=0)
            {
                e.flow-=Flow;
                edg[edges[x][i]^1].flow+=Flow;
                return Flow;
            }
        }

    }
    return 0;

}
long long dinic()
{
    long long ans=0;
    while(bfs())
    {
        memset(current,0,sizeof(current));
        long long flow;
        while(flow=dfs(S,LLONG_MAX))ans+=flow;
    }
    return ans;
}


int main()
{
    int m,n,t=0;
    int Map[35][35];
    int num[35][35];

    long long ans=0;

    scanf("%d %d",&m,&n);
    for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++)
    {
        scanf("%d",&Map[i][j]);
        num[i][j]=t++;
        ans+=Map[i][j];

    }

    S=t++;
    T=t++;

    for(int i=1;i<=m;i++)
    {
        for(int j=(i+1)%2+1;j<=n;j+=2)
        {
            addedge(S,num[i][j],Map[i][j]);

            if(i-1>=1)addedge(num[i][j],num[i-1][j],LLONG_MAX/2);
            if(i+1<=m)addedge(num[i][j],num[i+1][j],LLONG_MAX/2);
            if(j-1>=1)addedge(num[i][j],num[i][j-1],LLONG_MAX/2);
            if(j+1<=n)addedge(num[i][j],num[i][j+1],LLONG_MAX/2);

        }

    }

    for(int i=1;i<=m;i++)
    {
        for(int j=i%2+1;j<=n;j+=2)
        {
            addedge(num[i][j],T,Map[i][j]);
        }
    }


    printf("%lld",ans-dinic());
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/tian-luo/p/9501571.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值