《算法竞赛进阶指南》0x51线性DP I-区域

题目链接:https://www.acwing.com/problem/content/278/

题目给出一个n*m的矩阵,要求从中找出一个凸连通块,使得该连通块包含k个格子并且格子数之和最大,凸连通块由若干行构成,左右最多各有一个峰值。

可通过DP解决,每行处理完代表一个阶段,f[i,j,l,r,x,y]表示当前处理第i行,一共选择了j个格子,第i行选择的格子是[l,r],左右的状态是x,y表示下降或者上升。转移时要枚举每一种状态,并且枚举上一个阶段的左右位置。找到最大的一个,在加上第i行的静态的[l,r]的和。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 16;
int f[maxn][maxn*maxn][maxn][maxn][2][2];

struct State{//保存行数,格子数,左端点右端点,上升和递减的状态 
    int i,j,l,r,x,y;
}g[maxn][maxn*maxn][maxn][maxn][2][2];

int n,m,k;
int w[maxn][maxn];
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>w[i][j];
            
    for(int i=1;i<=n;i++)
        for(int j=0;j<=k;j++)
            for(int l=1;l<=m;l++)
                for(int r=l;r<=m;r++)
                {
                    if(j<(r-l+1))continue;
                    //左边扩张,右边扩张 
                    {
                        int& vf=f[i][j][l][r][1][0];
                        State& vg=g[i][j][l][r][1][0];
                        //找到从上一个状态扩展来的最大的一个状态 
                        for(int p=l;p<=r;p++)
                            for(int q=p;q<=r;q++)
                            {
                                int val=f[i-1][j-(r-l+1)][p][q][1][0];
                                if(vf<val){
                                    vf=val;
                                    vg={i-1,j-(r-l+1),p,q,1,0};//记录上一个状态 
                                }
                            }
                        for(int u=l;u<=r;u++)vf+=w[i][u]; 
                    }
                    //左边收缩,右边扩张 
                    {
                        int &vf=f[i][j][l][r][0][0];
                        State &vg=g[i][j][l][r][0][0];
                        //找到从上一个状态扩展来的最大的一个状态 
                        for(int p=1;p<=l;p++)
                            for(int q=l;q<=r;q++)
                                for(int x=0;x<=1;x++) 
                                {
                                    int val=f[i-1][j-(r-l+1)][p][q][x][0];
                                    if(vf<val){
                                        vf=val;
                                        vg={i-1,j-(r-l+1),p,q,x,0};//记录上一个状态 
                                    }
                                }
                        for(int u=l;u<=r;u++)vf+=w[i][u]; 
                    }
                    //左边收缩,右边收缩 
                    {
                        int &vf=f[i][j][l][r][0][1];
                        State &vg=g[i][j][l][r][0][1];
                        //找到从上一个状态扩展来的最大的一个状态 
                        for(int p=1;p<=l;p++)
                            for(int q=r;q<=m;q++)
                                for(int x=0;x<=1;x++)
                                    for(int y=0;y<=1;y++) 
                                    {
                                        int val=f[i-1][j-(r-l+1)][p][q][x][y];
                                        if(vf<val){
                                            vf=val;
                                            vg={i-1,j-(r-l+1),p,q,x,y};//记录上一个状态 
                                        }
                                    }
                        for(int u=l;u<=r;u++)vf+=w[i][u]; 
                    }
                    //左边扩张,右边收缩 
                    {
                        int &vf=f[i][j][l][r][1][1];
                        State &vg=g[i][j][l][r][1][1];
                        //找到从上一个状态扩展来的最大的一个状态 
                        for(int p=l;p<=r;p++)
                            for(int q=r;q<=m;q++)
                                for(int y=0;y<=1;y++) 
                                {
                                    int val=f[i-1][j-(r-l+1)][p][q][1][y];
                                    if(vf<val){
                                        vf=val;
                                        vg={i-1,j-(r-l+1),p,q,1,y};//记录上一个状态 
                                    }
                                }
                        for(int u=l;u<=r;u++)vf+=w[i][u]; 
                    }
                }
    
    int res=0;
    State state;
    //查找最大的终态 
    for(int i=1;i<=n;i++)
        for(int l=1;l<=m;l++)
            for(int r=1;r<=m;r++)
                for(int x=0;x<=1;x++)
                    for(int y=0;y<=1;y++)
                        if(f[i][k][l][r][x][y]>res){
                            res=f[i][k][l][r][x][y];
                            state={i,k,l,r,x,y};
                        }
    printf("Oil : %d\n",res);
    while(state.j){//该状态中还有没算到的格子 
        for(int i=state.l;i<=state.r;i++)
            printf("%d %d\n",state.i,i);
        state=g[state.i][state.j][state.l][state.r][state.x][state.y];
    }                            
    return 0;
}

 

posted @ 2020-07-28 10:17  WA自动机~  阅读(142)  评论(0编辑  收藏  举报