1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x51線性DP I-區域

《演算法競賽進階指南》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; }