动态规划:P1005[NOIP2007 提高组] 矩阵取数游戏 区间DP,高精度【模板】

P1005[NOIP2007 提高组] 矩阵取数游戏

分析与理解思路:

    因为每次是对n层的序列各取一个,所以我们可以看把每一层取m次看做独立的,对于每一层,其实都在区间DP的过程,每一层都收到更外层取的数字的制约,所以可以循环n次,对每一层都区间DP一下,这里的区间DP比较特殊,因为小区间才是答案,所以我们需要从大区间开始DP,而且对长度并不做要求,所以可以二维的循环枚举左右端点,注意是从大区间到小区间,所以右端点一定要从大到小,但是右端点不可能比左端点小,所以循环外层i 1->m;内层 j m->i;比如1 2 3 算出13 才能算出12 才能算出11 算出2 3 才能算出2 2。对于这一题dp[t][t],t==t时,就是最后删掉的数字是t的答案,所以每次要更新ans 取dp[t][t]t等于t的情况的最优解,再加到sum上,因为要取n层的ans加在一起。我们可以预处理的2的幂的数组,对于一个区间i,j 每次删掉的数字的次序就是m - (j - i)) - 1,x的就是2的m - (j - i)) - 1的幂次方。得到状态转换方程:

注意点:

    我们看数据范围:

  这题long long 都不够,需要用高精度,我从洛谷这题的题解中,向这位大佬:题解 P1005 【矩阵取数游戏】 - Jack_Homes_Huang 的博客 - 洛谷博客 (luogu.com.cn),学到了新的高精度的做法:4位压缩、重载+号,x号,高精度的max函数。

高精度结构体:

 

struct hp
{
    int p[500], len;
    hp()
    {
        memset(p, 0, sizeof(p));//构造函数 初始化高精度数组为0
        len = 0;
    }
    void print()
    {
        printf("%d", p[len]);
        for (int i = len - 1; i > 0; i--)
        {
            if (p[i] == 0)
            {
                printf("0000");
                continue;
            }
            for (int k = 10; k * p[i] < mod; k *= 10)
                printf("0");
            printf("%d", p[i]);
        }
    }//四位压缩的print
}

 

高精度+高精度的重载+号:

 

 1 hp operator+(const hp& a, const hp& b)
 2 {
 3     hp c;
 4     c.len = max(a.len, b.len);
 5     int x = 0;//用于进位
 6     for (int i = 1; i <= c.len; ++i)
 7     {
 8         c.p[i] = a.p[i] + b.p[i] + x;
 9         x = c.p[i] / mod;
10         c.p[i] %= mod;
11     }
12     if (x > 0)
13         c.p[++c.len] = x;//最高位可能再进位
14     return c;
15 }

 

高精度*单精度的重载*号

 
 1 hp operator*(const hp& a, int b)
 2 {
 3     hp c;
 4     c.len = a.len;
 5     int x = 0;//用于进位
 6     for (int i = 1; i <= c.len; ++i)
 7     {
 8         c.p[i] = a.p[i] * b + x;
 9         x = c.p[i] / mod;
10         c.p[i] %= mod;
11     }
12     while (x > 0)
13     {
14         c.p[++c.len] = x % mod;//最高位可能再进位
15         x /= mod;
16     }
17     return c;
18 }

 

高精度之间的max比较函数:

 1 hp max(const hp& a, const hp& b)
 2 {
 3     if (a.len > b.len)
 4         return a;
 5     else if (a.len < b.len)
 6         return b;
 7     for (int i = a.len; i > 0; i--)
 8         if (a.p[i] > b.p[i])
 9             return a;
10         else if (a.p[i] < b.p[i])
11             return b;
12     return a;//一样的话随便return 一个
13 }

 

完整AC代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
#define mod 10000
#define maxn 85
int num[maxn][maxn];
int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 3) + (x << 1) + (ch - '0');
        ch = getchar();
    }
    return x * f;
}
struct hp
{
    int p[500], len;
    hp()
    {
        memset(p, 0, sizeof(p));//构造函数 初始化高精度数组为0
        len = 0;
    }
    void print()
    {
        printf("%d", p[len]);
        for (int i = len - 1; i > 0; i--)
        {
            if (p[i] == 0)
            {
                printf("0000");
                continue;
            }
            for (int k = 10; k * p[i] < mod; k *= 10)
                printf("0");
            printf("%d", p[i]);
        }
    }//四位压缩的print
}dp[maxn][maxn], mi[maxn], ans;
//高精度+高精度
hp operator+(const hp& a, const hp& b)
{
    hp c;
    c.len = max(a.len, b.len);
    int x = 0;//用于进位
    for (int i = 1; i <= c.len; ++i)
    {
        c.p[i] = a.p[i] + b.p[i] + x;
        x = c.p[i] / mod;
        c.p[i] %= mod;
    }
    if (x > 0)
        c.p[++c.len] = x;//最高位可能再进位
    return c;
}
//高精度*单精度
hp operator*(const hp& a, int b)
{
    hp c;
    c.len = a.len;
    int x = 0;//用于进位
    for (int i = 1; i <= c.len; ++i)
    {
        c.p[i] = a.p[i] * b + x;
        x = c.p[i] / mod;
        c.p[i] %= mod;
    }
    while (x > 0)
    {
        c.p[++c.len] = x % mod;//最高位可能再进位
        x /= mod;
    }
    return c;
}
hp max(const hp& a, const hp& b)
{
    if (a.len > b.len)
        return a;
    else if (a.len < b.len)
        return b;
    for (int i = a.len; i > 0; i--)
        if (a.p[i] > b.p[i])
            return a;
        else if (a.p[i] < b.p[i])
            return b;
    return a;//一样的话随便return 一个
}
void createmi(int m)
{
    mi[0].p[1] = 1;
    mi[0].len = 1;
    for (int i = 1; i <= m + 1; ++i)
        mi[i] = mi[i - 1] * 2;
}
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            cin >> num[i][j];
    createmi(m);
    for (int k = 1; k <= n; ++k)
    {
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= m; ++i)
        {
            for (int j = m; j >= i; --j)
            {
                dp[i][j] = max(dp[i][j], dp[i - 1][j] + mi[(m - (j - i)) - 1] * num[k][i - 1]);
                dp[i][j] = max(dp[i][j], dp[i][j + 1] + mi[(m - (j - i)) - 1] * num[k][j + 1]);
            }
        }
        hp temp;
        for (int i = 1; i <= m; ++i)
        {
            temp = max(temp, dp[i][i] + mi[m] * num[k][i]);
        }
        ans = ans + temp;
    }
    ans.print();
    return 0;
}

 

 

posted @ 2022-04-21 11:31  朱朱成  阅读(70)  评论(0编辑  收藏  举报