Sgu167 I-country

题目描述

在 N*M 的矩阵中,每个格子有一个权值,要求寻找一个包含 K 个格子的凸连通块(连通块中间没有空缺,并且轮廓是凸的,如右图所示),使这个连通块中的格子的权值和最大。求出这个最大的权值和.N,M≤15,K≤225。

输入格式

第一行给出N,M,K,接下来给出这个矩阵

输出格式

如题


首先我们要明确凸连通块是什么:

它是一个连通块。从上往下看,它的左端点下标先递减后递增,右端点下标先递增后递减。

根据这一点,我们能够设计出这样的状态:

设dp(i,j,l,r,0/1,0/1)表示当前为第i行,已经用了j个格子,凸连通块在第i行的左端点为l,右端点为r,左端点现在在递增(0)还是递减(1),右端点在递增(0)还是递减(1)。那么可以列出状态转移方程:

\[len=r-l+1\\ dp[i][j][l][r][1][0]=\sum_{p=l}^{r}a[i][p]+Max_{l≤p≤q≤r}{\{}dp[i-1][j-len][p][q][1][0]{\}}\\ dp[i][j][l][r][1][1]=\sum_{p=l}^{r}a[i][p]+Max_{l≤p≤r≤q}{\{}Max_{0≤y≤1}{\{}dp[i-1][j-len][p][q][1][y]{\}}{\}}\\ dp[i][j][l][r][0][0]=\sum_{p=l}^{r}a[i][p]+Max_{p≤l≤q≤r}{\{}Max_{0≤x≤1}{\{}dp[i-1][j-len][p][q][x][0]{\}}{\}}\\ dp[i][j][l][r][0][1]=\sum_{p=l}^{r}a[i][p]+Max_{p≤l≤r≤q}{\{}Max_{0≤x≤1}{\{}Max_{0≤y≤1}{\{}dp[i-1][j-len][p][q][x][y]{\}}{\}}{\}} \]

初始化都为0,目标状态:Max{dp(i,K,l,r,x,y)}。

时间复杂度为O(N * M^4 * K)

#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 16
#define maxm 226
using namespace std;
 
int dp[maxn][maxm][maxn][maxn][2][2];
int n,m,sum,val[maxn][maxn];
int ans;
 
inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
 
int main(){
    n=read(),m=read(),sum=read();
    for(register int i=1;i<=n;i++){
        for(register int j=1;j<=m;j++) val[i][j]=val[i][j-1]+read();
    }
 
    for(register int i=1;i<=n;i++){
        for(register int j=1;j<=sum;j++){
            for(register int l=1;l<=m;l++){
                for(register int r=l;r<=m;r++){
                    if(j<r-l+1) break;
                    for(register int p=l;p<=r;p++){
                        for(register int q=p;q<=r;q++){
                            dp[i][j][l][r][1][0]=max(dp[i][j][l][r][1][0],dp[i-1][j-(r-l+1)][p][q][1][0]+val[i][r]-val[i][l-1]);
                        }
                    }
                    for(register int p=l;p<=r;p++){
                        for(register int q=r;q<=m;q++){
                            dp[i][j][l][r][1][1]=max(dp[i][j][l][r][1][1],max(dp[i-1][j-(r-l+1)][p][q][1][0],dp[i-1][j-(r-l+1)][p][q][1][1])+val[i][r]-val[i][l-1]);
                        }
                    }
                    for(register int p=1;p<=l;p++){
                        for(register int q=l;q<=r;q++){
                            dp[i][j][l][r][0][0]=max(dp[i][j][l][r][0][0],max(dp[i-1][j-(r-l+1)][p][q][0][0],dp[i-1][j-(r-l+1)][p][q][1][0])+val[i][r]-val[i][l-1]);
                        }
                    }
                    for(register int p=1;p<=l;p++){
                        for(register int q=r;q<=m;q++){
                            dp[i][j][l][r][0][1]=max(dp[i][j][l][r][0][1],max(dp[i-1][j-(r-l+1)][p][q][0][1],max(dp[i-1][j-(r-l+1)][p][q][1][0],max(dp[i-1][j-(r-l+1)][p][q][1][1],dp[i-1][j-(r-l+1)][p][q][0][0])))+val[i][r]-val[i][l-1]);
                        }
                    }
                    if(j==sum) ans=max(ans,max(dp[i][j][l][r][0][0],max(dp[i][j][l][r][1][0],max(dp[i][j][l][r][0][1],dp[i][j][l][r][1][1]))));
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-06-18 15:44  修电缆的建筑工  阅读(214)  评论(0编辑  收藏  举报