1. 程式人生 > >【CH5104】I-country 線性dp+路徑輸出

【CH5104】I-country 線性dp+路徑輸出

pre:在網格中,凸多邊形可以按行(row)分解成若干段連續的區間 [ l , r ] ,且左端點縱座標的值(col)滿足先減後增,右端點縱座標先增後減。
階段:根據這個小發現,可以將階段設定成每一行,因此,解決這個問題一共需要N個階段。
狀態:除了階段外,表示每一個狀態還需要記錄下當前階段下一共選了多少個網格,當前行選擇的區間 [ l , r ] ,和相對於上一行來說端點選擇的單調性。(0表示單調遞增,1表示單調遞減)
因此,狀態可以表示成為\(dp[i][j][l][r][x][y]\)
狀態轉移方程:分成四種情況進行討論,詳見程式碼。

第二個要實現的是路徑輸出,可以額外使用與狀態大小相同的陣列來記錄下當前狀態是從哪個狀態轉移而來的,最後從末狀態經過一次遞迴即可得到路徑。

程式碼如下

#include <bits/stdc++.h>
#define forto(i,a,b) for(i=a;i<=b;i++)//迴圈巨集定義,減小程式碼量
using namespace std;

int n,m,k,sum[20][20];
int f[16][226][16][16][2][2];
struct node{
    int l,r,x,y;
}pre[16][226][16][16][2][2];

void read_and_parse(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&sum[i][j]);
            sum[i][j]+=sum[i][j-1];
        }
}

int x,y,i,j,l,r,ans,ai,al,ar,ax,ay;//本題狀態數較多,因此採用當前變數全域性化,避免參數傳遞混亂

inline void update(int dat,int L,int R,int X,int Y){
    int& ans=f[i][j][l][r][x][y];//使用引用減小程式碼量
    node& p=pre[i][j][l][r][x][y];
    if(ans>=dat)return;
    ans=dat;
    p=(node){L,R,X,Y};
}

void print(int i,int j,int l,int r,int x,int y){
    if(!j)return;
    node& p=pre[i][j][l][r][x][y];
    print(i-1,j-(r-l+1),p.l,p.r,p.x,p.y);
    forto(j,l,r)printf("%d %d\n",i,j);
}

void solve(){
    forto(i,1,n)forto(j,1,k)forto(l,1,m)forto(r,l,m){
        int t=r-l+1;if(t>j)break;
        int now=sum[i][r]-sum[i][l-1];
        x=y=1;
        for(int p=l;p<=r;p++)
            for(int q=r;q<=m;q++){
                update(f[i-1][j-t][p][q][1][0]+now,p,q,1,0);
                update(f[i-1][j-t][p][q][1][1]+now,p,q,1,1);
            }
        x=1,y=0;
        for(int p=l;p<=r;p++)
            for(int q=p;q<=r;q++)
                update(f[i-1][j-t][p][q][1][0]+now,p,q,1,0);
        x=0,y=1;
        for(int p=1;p<=l;p++)
            for(int q=r;q<=m;q++){
                update(f[i-1][j-t][p][q][1][1]+now,p,q,1,1);
                update(f[i-1][j-t][p][q][1][0]+now,p,q,1,0);
                update(f[i-1][j-t][p][q][0][1]+now,p,q,0,1);
                update(f[i-1][j-t][p][q][0][0]+now,p,q,0,0);
            }
        x=y=0;
        for(int p=1;p<=l;p++)
            for(int q=l;q<=r;q++){
                update(f[i-1][j-t][p][q][1][0]+now,p,q,1,0);
                update(f[i-1][j-t][p][q][0][0]+now,p,q,0,0);
            }
    }
    forto(i,1,n)forto(l,1,m)forto(r,l,m)forto(x,0,1)forto(y,0,1)
        if(ans<f[i][k][l][r][x][y]){
            ans=f[i][k][l][r][x][y];
            ai=i,al=l,ar=r,ax=x,ay=y;
        }
    printf("Oil : %d\n",ans);
    print(ai,k,al,ar,ax,ay);//傳入終點狀態引數
}

int main(){
    read_and_parse();
    solve();
    return 0;
}