线性dp(数字三角形模型)

26 阅读2分钟

1027. 方格取数 - AcWing题库

思路

首先,这题其实就是找到从11nn最大ww和次大ww的和,但是走过的格子可以重复走,但走过就要把这个格子的价值变成00即可,可以考虑四维的状态转移方程dp[i1][j1][i2][j2]dp[i_1][j_1][i_2][j_2],但是再想一下就会发现我们考虑的所有状态都是在i1+j1==i2+j2i_1+j_1==i_2+j_2的情况下,因此横纵坐标的和相等,故我们考虑dp[k][i1][i2]dp[k][i_1][i_2]kk表示当横纵坐标之和,i1i_1i2i_2分别表示两次路径经过点的横坐标,然后纵坐标j1j_1,j2j_2可以用ki1k-i_1ki2k-i_2来表示,然后我们考虑状态转移,可以分为四部分,两条路径的点分别从前一个点转移过来,可以分为下下,下右,右下,右右这四种情况,即当前点石油前一个点经过以上操作得来的,点前的两个点左边为(i1,ki1),(i2,ki2)(i_1,k-i_1),(i_2,k-i_2),那么前一个点的状态对应分别为(i1,ki1),(i2,ki21)(i_1,k-i_1),(i_2,k-i_2-1)(i1,ki11),(i21,ki2)(i_1,k-i_1-1),(i_2-1,k-i_2)(i11,ki1),(i2,ki21)(i_1-1,k-i_1),(i_2,k-i_2-1)(i11,ki1),(i21,ki2)(i_1-1,k-i_1),(i_2-1,k-i_2),然后如果i1==i2i_1==i_2说明经过了同一个的点,那么我们就只能+w[i1][j1]+w[i_1][j_1],否则我们可以+(w[i1][j1]+w[i2][j2])+(w[i_1][j_1]+w[i_2][j_2])

代码

#include  <bits/stdc++.h>
using namespace std;
const int N=11;
int dp[N*2][N][N],n,w[N][N],a,b,c;
int main(){
    cin>>n;
    while(cin>>a>>b>>c){
        if(a==0 && b==0 && c==0) break;
        w[a][b]=c;
    }
    for(int k=2;k<=n*2;k++){//k一定要从2开始,因为(1,1)横纵坐标之和为2
        for(int i1=1;i1<=n;i1++){
            for(int i2=1;i2<=n;i2++){
                int j1=k-i1,j2=k-i2;
                if(j1<1 || j1>n || j2<1 || j2>n) continue;//保证j1和j2在[1,n]之间
                int t=w[i1][j1];
                if(j1!=j2) t+=w[i2][j2];//判断是否重合
                dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1-1][i2-1]+t);//下下
                dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1][i2-1]+t);//右下
                dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1-1][i2]+t);//下右
                dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1][i2]+t);//右右
            }
        }
    }
    cout<<dp[2*n][n][n];//最后横纵坐标加和=2*n,且都到(n,n)点的最大值。
    return 0;
}

AcWing 275. 传纸条 - AcWing

分析

这题和上一题几乎一模一样,思路也一样,但有几个细节不一样,要注意一下,第一,横纵坐标之和变成了n+mn+m,然后再后面两重循环的时候要注意ki1k-i_1ki2k-i_2要在[1,m][1,m]之间,所以由此求出kmi1k1k-m≤i_1≤k-1kmi2k1k-m≤i_2≤k-1,其他都一样。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=55;
int dp[N*2][N][N],w[N][N],n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>w[i][j];
        }
    }
    for(int k=2;k<=n+m;k++){
        for(int i1=max(1,k-m);i1<=min(k-1,n);i1++){
            for(int i2=max(1,k-m);i2<=min(k-1,n);i2++){
                int j1=k-i1,j2=k-i2;
                if(j1<1 || j1>m || j2<1 || j2>m) continue;
                int t=w[i1][j1];
                if(i1!=i2 || k==2 || k==m+n){
                    t+=w[i2][j2];
                    dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1-1][i2-1]+t);
                    dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1-1][i2]+t);
                    dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1][i2-1]+t);
                    dp[k][i1][i2]=max(dp[k][i1][i2],dp[k-1][i1][i2]+t);
                }
            }
        }
    }
    cout<<dp[n+m][n][n];
    return 0;
}

一些过于简单的题就不展示了QAQ!QAQ!