P2774 方格取数问题

Posted whymhe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2774 方格取数问题相关的知识,希望对你有一定的参考价值。

题目背景

none!

题目描述

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

程序运行结束时,将取数的最大总和输出

 

输入输出样例

输入样例#1: 复制
3 3
1 2 3
3 2 3
2 3 1 
输出样例#1: 复制
11

说明

m,n<=100

 

技术分享图片
//状压DP
//。貌似状压不能过,因为n,m都是<=100的,
//这样找出来就是100个二进制位,也就是2^100 
//.但是不知道为啥他们都能AC
//写写试试 

//91分封顶。
//数据还是太水。 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=1e2+5;
const int M=1e5+5;

inline int read()
{
    char c=getchar();int num=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        num=num*10+c-0;
    return num;
}

int n,m;
int map[N][N];
int sum[N][M];
int opt[N],cnt;
int dp[N][M];

void init(int line,int id)
{
    for(int i=1;i<=m;++i)
        if(opt[id]>>(i-1)&1)
            sum[line][id]+=map[line][i];
}

int main()
{
    n=read(),m=read();
    for(int i=0;i<(1<<m);++i)
    {
        if(!(i&(i<<1)||i&(i>>1)))    //找出不会有两个1相邻的状态 
            opt[++cnt]=i;
        if(i>2&&(i>>(i-1))&((i-1)>>(i-2)))
            i+=1<<i-2,--i;
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            map[i][j]=read();
    for(int i=1;i<=cnt;++i)
        init(1,i),dp[1][i]=sum[1][i];
    for(int i=2;i<=n;++i)
        for(int j=1;j<=cnt;++j)
            init(i,j);
    for(int i=2;i<=n;++i)    //枚举第几行 
    {
        for(int j=1;j<=cnt;++j)        //枚举第i行的状态 
        {
            for(int k=1;k<=cnt;++k)        //枚举第i-1行的状态 
            {
                if(opt[j]&opt[k])
                    continue;
                dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[i][j]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=cnt;++i)
    {
//        printf("%d %d\n",i,dp[n][i]);
        ans=max(ans,dp[n][i]);
    }
    printf("%d",ans);
    return 0;
}
91分状压

 

 

//网络流
//方格取数

//思考一下这个问题的性质
//就是个棋盘的黑白染色问题 
//假设(i+j)&1==1的格子是黑格
//那么我们可以建一个二分图
// 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue> 
using namespace std;

const int N=1e5+5;
const int INF=0x3f3f3f3f;

inline int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar())
        f=f==-?-1:f;
    for(;isdigit(c);c=getchar())
        num=num*10+c-0;
    return num*f;
}

int n,m,S,T;
int head[N],from[N],num_edge;
struct Edge
{
    int v,flow,nxt;
}edge[N];

void add_edge(int u,int v,int flow)
{
    edge[++num_edge].v=v;
    edge[num_edge].flow=flow;
    edge[num_edge].nxt=head[u];
    head[u]=num_edge;
}

int dep[N];
bool bfs()
{
    queue<int> que;
    int now,v;
    for(int i=S;i<=T;++i)
        dep[i]=0,from[i]=head[i];
    dep[S]=1;
    que.push(S);
    while(!que.empty())
    {
        now=que.front(),que.pop();
        for(int i=head[now];i;i=edge[i].nxt)
        {
            v=edge[i].v;
            if(!dep[v]&&edge[i].flow)
            {
                dep[v]=dep[now]+1;
                if(v==T)
                    return 1;
                que.push(v);
            }
        }
    }
    return 0;
}

int dfs(int now,int flow)
{
    if(now==T||!flow)
        return flow;
    int outflow=0,tmp;
    for(int i=head[now],v;i;i=edge[i].nxt)
    {
        if(!edge[i].flow)
            continue;
        v=edge[i].v;
        if(dep[v]!=dep[now]+1)
            continue;
        tmp=dfs(v,min(flow,edge[i].flow));
        if(!tmp)
            continue;
        edge[i].flow-=tmp;
        edge[i^1].flow+=tmp;
        flow-=tmp;
        outflow+=tmp;
        if(!flow)
            break;
    }
    dep[now]=0;
    return outflow;
}

#define A m*(i-1)+j
int ans;
int main()
{
    num_edge=1;
    n=read(),m=read();
    T=n*m+1;
    for(int i=1,a;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            a=read();
            ans+=a;
            if((i+j)%2==0)
            {
                add_edge(A,T,a);
                add_edge(T,A,0);
                continue;
            }
            add_edge(S,A,a);
            add_edge(A,S,0);
            if(i>1)
            {
                add_edge(A,A-m,INF);
                add_edge(A-m,A,0);
            }
            if(j>1)
            {
                add_edge(A,A-1,INF);
                add_edge(A-1,A,0);
            }
            if(i<n)
            {
                add_edge(A,A+m,INF);
                add_edge(A+m,A,0);
            }
            if(j<m)
            {
                add_edge(A,A+1,INF);
                add_edge(A+1,A,0);
            }
        }
    }
    while(bfs())
    {
        ans-=dfs(S,INF);
    }
    printf("%d",ans);
    return 0;
}

 

以上是关于P2774 方格取数问题的主要内容,如果未能解决你的问题,请参考以下文章

P2774 方格取数问题

P2774 方格取数问题

洛谷P2774 方格取数问题

洛谷 P2774 方格取数问题

[洛谷P2774] 方格取数问题

P2774 方格取数问题 网络流重温