数字三角形模型

数字三角形模型

数字三角形这个题为原型的所有题型。


AcWing 898.数字三角形

原题链接:https://www.acwing.com/problem/content/900/

解题思路

将这个数字三角形看成一个矩阵,然后DP分析

image

代码
#include<iostream>
#include<cstring>

using namespace std;

const int N = 510;
const int INF = 1e9;

int f[N][N],a[N][N];
int n;

int main()
{
    scanf("%d",&n);
    for(int i = 0; i <= n; i++)
        for(int j = 1; j <= i;j ++)
            scanf("%d",&a[i][j]);
    
    // 因为存在负数,所以两边也要初始化为负无穷
    for(int i = 0; i <= n; i ++)
        for(int j = 0; j <= i + 1; j ++)
            f[i][j] = -INF;
    f[1][1] = a[1][1];
    
    for(int i = 2; i <= n; i ++)
        for(int j = 1; j <= i; j ++)
            f[i][j] = a[i][j] + max(f[i-1][j-1],f[i-1][j]);
    
    int res = -INF;
    for(int j = 1; j <= n; j ++) res = max(res,f[n][j]);
    
    printf("%d",res);
    
    return 0;
}

AcWing 1015. 摘花生

原题链接:https://www.acwing.com/problem/content/1017/

思路

image

代码
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 110;

int t;
int f[N][N],w[N][N];
int r,c;

int main()
{
    cin >> t;
    
    while(t --)
    {
        cin >> r >> c;
        
        for(int i = 1; i <= r; i ++)
            for(int j = 1; j <= c; j ++)
                cin >> w[i][j];
        
        memset(f,0,sizeof f);
        
        for(int i = 1; i <= r; i ++)
        {
            for(int j = 1; j <= c; j ++)
             {
                 f[i][j] = w[i][j] + max(f[i-1][j],f[i][j-1]);
             }
        }
        cout << f[r][c] << endl;
    }
    
    return 0;
}

AcWing 1018. 最低通行费

原题链接:https://www.acwing.com/problem/content/1020/

思路

阅读理解:左上角到右下角,时间不超过2n-1,说明只能向下或者向右走,不能回头 => 摘花生问题

image

边界处理时,第一行和第一列只能从一遍过来,不能从外面过来(外面的值都是正无穷)

代码
#include<iostream>
#include<cstring>

using namespace std;

const int N = 110;

int n,f[N][N],w[N][N];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            cin >> w[i][j];
    
    memset(f,0x3f,sizeof f);
    
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 1;j <= n; j ++)
        {
            if(i == 1 && j == 1) f[1][1] = w[1][1];
            else f[i][j] = w[i][j] + min(f[i-1][j],f[i][j-1]);
        }
    }
    
    cout << f[n][n];
    
    return 0;
}

AcWing 1027. 方格取数

原题链接:https://www.acwing.com/problem/content/1029/

思路

虽然以摘花生为原型,但是不可以分两次摘花生
先看一个例子,
比如:

0  1  0
2  2  2
1  0  0

如果分两次,第一次最优肯定是把三个2取到然后结束,第二次就只能取一个1。
如果同时来,那么是可以把所有的数都取下的。

因为第一次摘花生,第一次的最优解已经影响到第二次的最优解了。两次分开走并不是全局最优的。只能dp同时走才可以找到全局最优方案。

所以我们只能同时行进去找最优解。(相当于让两个人同时走)
对于重复计算,只有两个人走到同一个格子的时候就会重复。

两个人走的步数一样的时候,有可能走到同一个格子 => i1+j1 == i2+j2 可能重复计算

同时走状态表示就是f[i1,j1,i2,j2],如果同时走到一个格子,肯定i1+j1 == i2+j2, 两个人同时走,走的步数总是一样的,令k = i1+j2 = i2+j2,可以优化掉一维。

image

代码
#include<iostream>
#include<cstring>

using namespace std;

const int N = 15;
int f[N*2][N][N],w[N][N];
int n;

int main()
{
    cin >> n ;
    
    int a,b,c;
    while(cin >> a >> b >> c, a || b || c) w[a][b] = c;
    
    for(int k = 2; k <= n + n; k ++) // 从[1,1]开始走
    {
        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)
                {
                    int t = w[i1][j1];
                    if(i1 != i2) t += w[i2][j2];
                    int &x = f[k][i1][i2]; // 每次写太麻烦,直接写个引用
                    x = max(x,f[k-1][i1-1][i2-1] + t);
                    x = max(x,f[k-1][i1-1][i2] + t);
                    x = max(x,f[k-1][i1][i2] + t);
                    x = max(x,f[k-1][i1][i2-1] + t);
                }
            }
        }
    }
    cout << f[n+n][n][n];
    
    return 0;
}

AcWing 275. 传纸条

原题链接:https://www.acwing.com/problem/content/277/

思路

大致思路和 方格取数 这一题相同

需要注意的是,本题是n×m的矩阵。

k 的取值是: k <= n + m
x1,x2的范围应该是:1 <= x <= n
y1,y2的范围应该是:1 <= y <= m

y1 = k - x1
1 <= k - x1 <= m
所以 x1 <= k - 1 , x1 >= k - m
即 max(1,k-m) <= x1 <= min(n,k-1)

最后的答案是f[n+m][n][n]:f[n+m][n][n]代表第一次从(1,1)走到(n,k-n),第二次从(1,1)走到(n,k-n)

代码
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 55;

int f[N+N][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 ++) // 从[1,1]开始走,防止数组越界
    {
        for(int i1 = max(1,k-m); i1 <= min(n,k-1); i1 ++)
        {
            for(int i2 = max(1,k-m); i2 <= min(n,k-1); i2 ++)
            {
                int j1 = k - i1,j2 = k - i2;
                int t = w[i1][j1];
                if(i1 != i2) t += w[i2][j2];
                
                // 四种情况,可以用个小循环枚举出
                for(int a = 0; a <= 1; a ++)
                    for(int b = 0; b <= 1; b ++)
                        f[k][i1][i2] = max(f[k][i1][i2],f[k-1][i1 - a][i2 - b] + t);
            }
        }
    }
    cout << f[n+m][n][n] ; // f[n+m][n][n]代表第一次从(1,1)走到(n,k-n),第二次从(1,1)走到(n,k-n)
    
    return 0;
}


posted @ 2022-10-03 11:47  r涤生  阅读(4)  评论(0编辑  收藏  举报