洛谷DP专题

P1004 方格取数
大意:找出从矩阵左上到右下获得分数最大的两条路径,求其最大分数

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;

int n,x,y,z,a[20][20];
int f[15][15][15][15];

int main()
{
    scanf("%d",&n);
    memset(a,0,sizeof(a));
    while(1)
    {
        scanf("%d%d%d",&x,&y,&z);
        if(!x && !y && !z) break;
        a[x][y] = z;
    }
    memset(f,0,sizeof(f));
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= n; ++j)
            for(int k = 1;k <= n; ++k)
                for(int l = 1;l <= n; ++l)
                {
                    f[i][j][k][l] = max(f[i - 1][j][k - 1][l],max(f[i - 1][j][k][l - 1],
                                    max(f[i][j - 1][k - 1][l],f[i][j - 1][k][l - 1])));
                    f[i][j][k][l] += a[i][j] + a[k][l];
                    if(i == k && j == l) f[i][j][k][l] -= a[i][j];  //排除掉两个点重合的情况
                }
    printf("%d\n",f[n][n][n][n]);
    return 0;
}

P1005 矩阵取数游戏 //区间DP
题目描述:
对于一个给定的n×m的矩阵,矩阵中的每个元素a{i,j}均为非负整数。游戏规则如下:
(1)每次取数时须从每行各取走一个元素,共nn个。经过mm次后取完矩阵内所有元素;
(2)每次取走的各个元素只能是该元素所在行的行首或行尾;
每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 *2^i,其中i表示第i次取数(从1开始编号);
(3)游戏结束总得分为m次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分

分析:
高精度太难写了(写个60分的吧)
f[i][j] = max(f[i - 1][j] + a[i - 1] * 2 ^(m - j + i - 1), f[i][j + 1] + a[j + 1] * 2 ^ (m - j + i - 1));
注意这种方式区间是从大到小的,所以结束条件是取这一行 f[i][j] + a[i] * 2 ^ m 的最大值


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;

long long f[100][100];
long long a[100][100];
int n,m,k;
long long ans;

long long maxi(long long x,long long y)
{
    if(x > y) return x;
    return y;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= m; ++j)
            scanf("%d",&a[i][j]);
    memset(f,0,sizeof(f));
    ans = 0;
    for(k = 1;k <= n; ++k)
    {
         for(int i = 1;i <= m; ++i)
            for(int j = m; j >= i; --j)
            {
                f[i][j] = maxi(f[i - 1][j] + pow(2 , m - j + i - 1) * a[k][i - 1],
                               f[i][j + 1] + pow(2 , m - j + i - 1) * a[k][j + 1]);
            }
        long long t = 0;
        for(int i = 1;i <= m; ++i)
            t = maxi(f[i][i] + a[k][i] * pow(2,m),t);
        ans += t;
    }
    printf("%lld\n",ans);
    return 0;
}

还有另一种方式f[i][len],表示从i开始,长度为len的区间max值

int n, m;
__int128 game[MAXN][MAXN];

__int128 f[MAXN][MAXN];
__int128 solve(__int128 a[])
{
    memset(f,0,sizeof(f));
    for(int len=0;len<=m;++len)
        for(int i=1;i+len<=m;++i)
            f[i][i+len]=max(2*f[i+1][i+len]+2*a[i],2*f[i][i+len-1]+2*a[i+len]);
    return f[1][m];
}

P1018 乘积最大
题目大意:
有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。

分析:
还是高精度,不写hhh 60分
f[i][j]表示在前i个位置插入了j个乘号所得到的最大乘积
枚举上一个乘号出现的位置
f[i][j] = max(f[l][j - 1] * s[l + 1][i]) 0 <= l < i

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
#include<vector>
using namespace std;

long long f[50][50];
string a;
long long s[50][50];
int n,k;

int main()
{
    scanf("%d%d",&n,&k);
    cin>>a;
    f[0][0] = (int)(a[0] - '0');
    for(int i = 0;i < n; ++i)
        for(int j = i; j < n; ++j)
            s[i][j] = s[i][j - 1] * 10 + (int)(a[j] - '0');
    for(int i = 1;i < n; ++i)
        for(int j = 0;j <= k; ++j)
        {
            if(j == 0) f[i][j] = f[i - 1][j] * 10 + (int)(a[i] - '0');
            else
            {
                for(int l = 0;l < i; ++l)
                f[i][j] = max(f[i][j],f[l][j - 1] * s[l + 1][i]);
            }
        }
    printf("%lld\n",f[n - 1][k]);
    return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_29904713/article/details/88358181