P1005 [NOIP2007 提高组] 矩阵取数游戏(区间dp+__int128)

传送门

思路不解释了:大佬的题解
看到这题__int128也能过,想拿__int128试试。
遇到的小坑:

f[i][j] = max(f[i - 1][j] + (1 << (m - j + i - 1)) * d[i - 1], f[i][j]);
f[i][j] = max(f[i][j + 1] + (1 << (m - j + i - 1)) * d[j + 1], f[i][j]);

上面的写法是错的(不知道怎么解释

f[i][j] = max(f[i - 1][j] + (d[i - 1] << (m - j + i - 1)), f[i][j]);
f[i][j] = max(f[i][j + 1] + (d[j + 1] << (m - j + i - 1)), f[i][j]);

这样才是正确的写法

#include <bits/stdc++.h>

using namespace std;
//-----pre_def----
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = a; i <= b; i++)
#define rif(i, a, b) for (int i = a; i >= b; i--)
#define init_h memset(h, -1, sizeof h), idx = 0;
//---------------
const int N = 85;
int n, m;
__int128 f[N][N];
__int128 d[N];
void output(__int128 x)
{
    if (x > 9)
        output(x / 10);
    putchar(x % 10 + '0');
}
__int128 read()
{
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    int StartTime = clock();
#endif
    cin >> n >> m;
    __int128 ans = 0;
    while (n--)
    {
        memset(f, 0, sizeof f);
        fir(i, 1, m)
            d[i] = read();
        fir(i, 1, m)
            rif(j, m, i)
        {
            f[i][j] = max(f[i - 1][j] + (d[i - 1] << (m - j + i - 1)), f[i][j]);
            f[i][j] = max(f[i][j + 1] + (d[j + 1] << (m - j + i - 1)), f[i][j]);
        }

        __int128 maxn = 0;
        fir(i, 1, m)
            maxn = max(maxn, (f[i][i] + (d[i] << m)));
        ans += maxn;
    }
    output(ans);
#ifndef ONLINE_JUDGE
    printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
    return 0;
}

(还是太菜了 要多刷题)

# P1005 [NOIP 2007 提高] 矩阵游戏 ## 题目描述 帅帅经常跟同学玩一个矩阵游戏:对于一个给定的 $n \times m$ 的矩阵矩阵中的每个元素 $a_{i,j}$ 均为非负整游戏规则如下: 1. 每次时须从每行各走一个元素,共 $n$ 个。经过 $m$ 次后矩阵内所有元素; 2. 每次走的各个元素只能是该元素所在行的行首或行尾; 3. 每次都有一个得分值,为每行的得分之和,每行的得分 = 被走的元素值 $\times 2^i$,其中 $i$ 表示第 $i$ 次(从 $1$ 开始编号); 4. 游戏结束总得分为 $m$ 次得分之和。 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出后的最大得分。 ## 输入格式 输入文件包括 $n+1$ 行: 第一行为两个用空格隔开的整 $n$ 和 $m$。 第 $2\sim n+1$ 行为 $n \times m$ 矩阵,其中每行有 $m$ 个用单个空格隔开的非负整。 ## 输出格式 输出文件仅包含 $1$ 行,为一个整,即输入矩阵后的最大得分。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 3 1 2 3 3 4 2 ``` ### 输出 #1 ``` 82 ``` ## 说明/提示 **【据范围】** 对于 $60\%$ 的据,满足 $1\le n,m\le 30$,答案不超过 $10^{16}$。 对于 $100\%$ 的据,满足 $1\le n,m\le 80$,$0\le a_{i,j}\le1000$。 **【题目来源】** NOIP 2007 提高第三题。 #include <bits/stdc++.h> using namespace std; int n , m; int num[85][85]; long long sc = 0; int main(){ cin >> n >> m; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ cin >> num[i][j]; if(num[i][j] <= num[i][j - 1]){ int k = 1; while(k < j){ if(num[i][j - k + 1] <= num[i][j - k]){ swap(num[i][j - k + 1] , num[i][j - k]); }else{ break; } k++; } } } int k = 1; for(int j = 1;j <= m;j++){ k *= 2; sc += k * num[i][j]; } } cout << sc; return 0; } 我的代码哪错了
最新发布
07-10
<think>首先,用户的问题是关于一个编程问题:P1005 [NOIP 2007 提高] 矩阵游戏。用户提供了题目描述和他的代码,并问“我的代码哪错了”。 我需要分析用户的代码。代码是用C++写的,目的是计算矩阵游戏的最大得分。 题目规则: - 有一个n行m列的矩阵,每个元素是非负整。 - 每次,从每行一个元素,要么是行首,要么是行尾。 - m次,完所有元素。 - 每次的得分是:被走的元素值乘以2的i次方,i是第i次(从1开始)。 - 总得分是m次得分的和。 用户代码的主要部分: 1. 输入n和m。 2. 输入矩阵,但在输入过程中,对于每一行,当输入一个新元素时,如果它小于或等于前一个元素,就尝试通过交换将它插入到正确位置,类似于插入排序。但这不是排序,因为题目没有要求排序。 3. 然后,对于每一行,计算得分:sc += k * num[i][j],其中k初始为1,每次乘以2(所以k是2^j,j从1开始)。 代码中: ```cpp for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ cin >> num[i][j]; if(num[i][j] <= num[i][j - 1]){ // 注意:j从1开始,num[i][0]未初始化,可能为0 int k = 1; while(k < j){ if(num[i][j - k + 1] <= num[i][j - k]){ swap(num[i][j - k + 1] , num[i][j - k]); }else{ break; } k++; } } } int k = 1; for(int j = 1;j <= m;j++){ k *= 2; // k = 2^j sc += k * num[i][j]; // 所以这里sc累加的是2^j * num[i][j] } } ``` 问题: 1. **输入处理中的排序逻辑**:用户似乎在输入时对每一行进行部分排序,但这不是题目要求的。题目没有说需要对行排序。实际上,的顺序依赖于每次行首或行尾,所以顺序很重要。用户代码试图将行排序为非递减序列?但交换逻辑是:如果新输入的元素小于等于前一个,就向前交换直到它大于前一个元素。这实际上是在构建一个非递减序列?但代码中,如果num[i][j] <= num[i][j-1],就交换它们,然后继续向前比较。这类似于插入排序,但目的是什么?题目没有要求行有序。 2. **得分计算错误**:在计算得分时,用户简单地遍历每一行的每个元素,乘以2^j(j是列索引)并累加。但题目中,得分依赖于的顺序(第i次),而不是列索引。更重要的是,时每次从行首或行尾,所以元素的顺序在过程中会改变。用户代码固定了行的顺序(通过那个排序逻辑),然后按顺序计算得分,但忽略了策略(选择行首或行尾)。 关键点:题目要求的是最大得分,需要通过动态规划或其他方法决定每次行首还是行尾,以最大化得分。 在用户代码中,没有实现正确的策略。它只是对每一行排序(部分排序),然后按顺序乘以2^j累加。这完全忽略了顺序的影响。 例如,输入样例: 2 3 1 2 3 3 4 2 用户代码: - 对于第一行:输入1,2,3。由于每个新元素都大于前一个,所以没有交换。然后计算得分:k=1*2=2, sc+=2*1=2; k=4, sc+=4*2=8; k=8, sc+=8*3=24; 总sc=2+8+24=34? 但第一行得分应该是:第一次1(2^1*1=2),第二次2(2^2*2=8),第三次3(2^3*3=24),总34。但这是按顺序的,如果策略不同,得分可能不同。但在这个例子中,行已经有序,行首或行尾:如果总是行首,就是1,2,3;如果行尾,是3,2,1,得分不同。 在用户代码中,对于第一行,无论输入顺序,它都试图排序。但在输入时,1,2,3已经有序,所以没有交换。计算得分:j=1: k=2, 1*2=2;j=2: k=4,2*4=8;j=3: k=8,3*8=24;总34。 但题目中,策略:我们可以选择行首或行尾。例如,对于一行[1,2,3],可能的法: - 行首三次:1,2,3 → 2^1*1 + 2^2*2 + 2^3*3 = 2 + 8 + 24 = 34 - 行尾三次:3,2,1 → 2^1*3 + 2^2*2 + 2^3*1 = 6 + 8 + 8 = 22 - 混合:例如,先行首1,然后行尾3,然后行中2(但行中不可,只能行首或行尾,所以当走1后,行变成[2,3],然后行尾3,再2。顺序:1,3,2 → 2^1*1 + 2^2*3 + 2^3*2 = 2 + 12 + 16 = 30 或者先行尾3,然后行首1,然后2:3,1,2 → 6 + 4 + 16 = 26 最大是34。 在用户代码中,它计算了34,但它是按输入顺序或排序后的顺序计算,而不是根据实际策略。 对于第二行:输入3,4,2。 输入3:没有前一个。 输入4:4>3,不交换。 输入2:2<=4?是,所以交换:比较j=3,num[i][3]=2, num[i][2]=4,2<=4,所以交换:swap(num[i][3], num[i][2]),现在行变成[3,2,4]?然后while循环:k=1,比较位置j-k+1=3-1+1=3? 和 j-k=2? 索引从1开始。 初始:j=1: num[1]=3 j=2: num[2]=4 (输入4>3,不交换) j=3: 输入2,if(2<=4) true,进入while: k=1: 比较 num[i][3-1+1]=num[i][3]=2 和 num[i][3-1]=num[i][2]=4?条件:if(num[i][j - k + 1] <= num[i][j - k]) 即 if(2<=4) true,所以交换 num[i][3] 和 num[i][2]。现在:位置2是2,位置3是4?但j=3对应位置3。 交换后,为:索引1:3, 索引2:2, 索引3:4?然后k++,k=2<3? 是,继续:现在比较 num[i][3-2+1]=num[i][2]=2 和 num[i][3-2]=num[i][1]=3?if(2<=3) true,所以交换 num[i][2] 和 num[i][1]。现在:索引1:2, 索引2:3, 索引3:4?然后k++,k=3<3? 否,退出。 所以第二行被排序为[2,3,4]。 然后计算得分:k=1; j=1: k=2*2=4? sc+=2*2=4? 等等: k初始1。 j=1: k*=2 → k=2, sc += 2 * num[i][1] = 2*2=4 j=2: k*=2 → k=4, sc += 4*3=12 j=3: k*=2 → k=8, sc +=8*4=32 总sc第二行=4+12+32=48 总sc=第一行34+第二行48=82,但输出是82,样例输出82。 在样例中,用户代码输出82,正确。 但这是巧合吗?因为用户代码对行进行了排序(升序),然后按顺序计算得分。但排序后是升序,乘以2^j,j从1到m,2^j递增,所以小的乘小的权重,大的乘大的权重,这似乎是合理的,因为我们要最大化得分,所以希望大的在后期(乘更大的2^i)。 在动态规划解中,通常我们定义dp[l][r]为从行中区间[l,r]的最大得分,其中l和r是当前行首和行尾的索引。 状态转移:dp[l][r] = max( num[l]*2^k + dp[l+1][r], num[r]*2^k + dp[l][r-1] ),但k决于,k从m开始递减?不,k是第几次,从1到m。 但k在区间长度变化时变化。标准做法是:设当前是第t次,t = m - (r - l) ?因为初始区间长度m,一次长度减1,所以当区间[l,r]时,已经了 m - (r-l+1) 次,所以当前是第 (m - (r-l)) 次?设len = r-l+1,则已经了 m - len 次,所以当前是第 (m - len + 1) 次?因为总次m,剩余len时,已 m-len 次,所以当前是第 (m-len+1) 次。 例如,m=3,初始len=3,第一次,t=1;后len=2,则下次时t=2;len=1时t=3。 所以 t = m - len + 1 = m - (r-l+1) + 1 在dp中,我们可以用区间长度作为阶段。 但用户代码没有dp,而是简单排序。 在样例中,它工作了。但为什么?因为排序后升序,然后按顺序(相当于总是行首),但乘以2^j,j从1到m,2^j递增,所以小的(乘小权重),大的(乘大权重),这确实最大化得分,因为大乘大权重。 在动态规划中,最优策略确实是先两端的较小者,以保留大到后面大权重。但在这个特定权重(指增长)下,最优策略是两端中较小者,以便大留到后面。 更精确地说,对于一行,dp[l][r] = max( num[l]*2^{t} + dp[l+1][r], num[r]*2^{t} + dp[l][r-1] ),其中t是当前。 但用户代码通过排序,将行变成升序,然后假设总是从行首(即最小者),这应该是最优的,因为走小的,留下大的给后面。 在样例第二行[3,4,2],排序后[2,3,4],然后:2,3,4 → 2^1*2 + 2^2*3 + 2^3*4 = 4 + 12 + 32 = 48 实际最优:如果行尾2(值2),但位置是行尾,当前行[3,4,2],行尾是2,2:2^1*2=4,然后行[3,4],行尾4:2^2*4=16,然后3:2^3*3=24,总4+16+24=44?但行首3:2^1*3=6,然后行[4,2],行尾2:2^2*2=8,然后4:2^3*4=32,总6+8+32=46;或者行首3,然后行首4,然后2:6+16+8=30?混合:先行尾2(2),然后行首3(3),然后4:2+12+32=46?计算:第一次2:2*2=4;第二次行[3,4],行首3:3*4=12;第三次4:4*8=32;总48?但行[3,4],行首3是3*4=12,然后4是4*8=32;如果行尾4:4*4=16,然后3:3*8=24,总4+16+24=44。所以最大是48?但48怎么得到?先行首3?但3不是最小。最优策略:第一次行尾2(2),然后行变成[3,4],第二次行尾4(4*4=16),然后3(3*8=24),总4+16+24=44;或第二次行首3(3*4=12),然后4(4*8=32),总4+12+32=48。所以48是可能的:法:第一次行尾2(位置3),然后行首3(位置1),然后4(位置2)。顺序:2,3,4 → 得分:2^1*2 + 2^2*3 + 2^3*4 = 2*2 + 4*3 + 8*4 = 4+12+32=48。在用户代码中,排序后[2,3,4],然后按顺序,相当于行首三次:2,3,4,得分相同。 在动态规划中,dp[1][3]:max( num[1]*2^1 + dp[2][3], num[3]*2^1 + dp[1][2] ) 设num[1]=3, num[2]=4, num[3]=2 dp[2][3]:区间[4,2],len=2,t=2(因为m=3,剩余2,当前是第2次dp[2][3] = max(4*4 + dp[3][3], 2*4 + dp[2][2])dp[i][i]是最后一个元素,t=3,2^3*num[i] 更仔细: 定义dp[l][r] 为区间[l,r]的最大得分,当前是 t = m - (r-l) ?初始时l=1,r=3,len=3,t=1(第一次) 标准状态:设f[l][r] 表示当前区间[l,r],已经了k次(k=m - (r-l+1)),则当前是第k+1次。 或者用区间长度d,d从1到m。 设d = r-l+1 则当前是第 (m - d + 1)。 则 f[l][r] = max( num[l] * 2^{m-d+1} + f[l+1][r], num[r] * 2^{m-d+1} + f[l][r-1] ) 边界:当l=r时,f[l][r] = num[l] * 2^{m} ?因为d=1,m-d+1=m,最后一次。 对于[3,4,2]: m=3 f[1][3]:d=3,t=m-3+1=1 = max( num[1]*2^1 + f[2][3], num[3]*2^1 + f[1][2] ) f[2][3]:d=2,t=3-2+1=2 = max( num[2]*2^2 + f[3][3], num[3]*2^2 + f[2][2] ) f[3][3]:d=1,t=3-1+1=3,f[3][3]=num[3]*2^3=2*8=16 f[2][2]:d=1,t=3,num[2]*8=4*8=32 f[2][3] = max(4*4 + 16, 2*4 + 32) = max(16+16=32, 8+32=40) =40?但num[2]=4, num[3]=2 f[2][3] = max( num[2]*4 + f[3][3], num[3]*4 + f[2][2] ) = max(4*4 + 16, 2*4 + 32) = max(16+16=32, 8+32=40)=40 但f[2][2]是当区间[2,2],即元素4,t=3,4*8=32,正确。 f[1][2]:区间[3,4],d=2,t=2 f[1][2] = max( num[1]*4 + f[2][2], num[2]*4 + f[1][1] ) = max(3*4 + 32, 4*4 + f[1][1]) f[1][1]:num[1]*8=3*8=24 所以 max(12+32=44, 16+24=40) =44 现在回f[1][3]= max( num[1]*2 + f[2][3], num[3]*2 + f[1][2] ) = max(3*2 + 40, 2*2 + 44) = max(6+40=46, 4+44=48) =48 所以最大48,正确。 在用户代码中,通过排序行[3,4,2]为[2,3,4],然后按顺序,得分48,相同。 但这是否总是正确?即,是否总是最优策略是最小元素,无论位置? 考虑一个反例:假设一行[1,100,1] m=3 最优策略: - 如果先行首1:得分2^1*1=2,然后行[100,1],行尾1:2^2*1=4,然后100:2^3*100=800,总806 - 如果先行尾1:2^1*1=2,然后行[1,100],行首1:4*1=4,然后100:800,总806 - 如果先行首1,然后行首100:2^1*1 + 2^2*100 + 2^3*1 = 2 + 400 + 8 = 410 - 或先行尾1,然后行尾100:2 + 400 + 8=410 所以最大是806 在用户代码中,输入行[1,100,1],当输入第一个1:无交换。 输入100:100>1,不交换。 输入1:1<=100,所以交换:比较j=3,num[3]=1 和 num[2]=100,1<=100,交换,行变成[1,1,100]?然后k++,k=2<3,比较num[2]=1 和 num[1]=1,1<=1,交换,但相同,交换后还是[1,1,100]。 排序后[1,1,100] 得分:j=1: k=2, sc+=2*1=2 j=2: k=4, sc+=4*1=4 j=3: k=8, sc+=8*100=800 总806,正确。 另一个反例:假设[10,1,10] m=3 最优: - 先行首10:2*10=20,然后行[1,10],行首1:4*1=4,然后10:8*10=80,总104 - 先行尾10:2*10=20,然后行[10,1],行尾1:4*1=4,然后10:80,总104 - 混合:先行首10,然后行尾10:20 + 40 + 8*1=20+40+8=68? 顺序:行首10(20),然后行尾10(4*10=40),然后1(8),总68 或先行尾10(20),然后行首10(40),然后1(8),68 - 先中间?不能。所以最大104。 用户代码:输入10,输入1:1<=10,交换:比较j=2,num[2]=1 和 num[1]=10,1<=10,交换,行[1,10]?然后输入10:10>1? 不,10>10? 10<=10? 是,所以交换:比较num[3]=10 和 num[2]=10,10<=10,交换(相同),然后k++,比较num[2]=10 和 num[1]=1,10>1? 不,10<=1? 假,所以停止。行变成[1,10,10]?交换后:初始输入10,1:交换后[1,10];输入10:10<=10? 是,交换num[3]和num[2]:位置3和2都是10,交换后相同;然后k=1<3,比较num[2]和num[1]:10和1,10<=1? 假,停止。所以行[1,10,10] 得分:2*1 + 4*10 + 8*10 = 2+40+80=122?但最优是104,122>104,但122可能吗? 计算:如果顺序:1,10,10 → 2^1*1 + 2^2*10 + 2^3*10 = 2 + 40 + 80 = 122 但实际行是[10,1,10],如何到1,10,10?只能两端。 从[10,1,10],行首10:得20,行[1,10];行首1:4,行[10];10:80,总104。 行尾10:20,行[10,1];行尾1:4,行[10];10:80,总104。 行首10:20,行[1,10];行尾10:40,行[1];1:8,总68。 不能直接中间1。所以最大是104,但用户代码计算122,这是不可能的,因为元素是10,1,10,总和21,最大权重8*10=80,但122>80+? 不合理。 用户代码错误:在排序时,它改变了行的顺序,但题目中,行是给定的,我们必须从实际位置行首或行尾,不能重新排序。 在[10,1,10]例子中,用户代码排序后[1,10,10],但实际行是[10,1,10],所以位置不同。用户代码假设行首是1,但实际行首是10。 所以用户代码错误地修改了行,导致计算了错误的序列。 在得分计算中,它使用排序后的行,按索引顺序,但实际游戏中,我们只能当前行首或行尾,不能内部元素。 在[1,10,10]中,按顺序1,10,10,但实际行[10,1,10],行首是10,行尾是10,不能直接1,除非先走一个端。 所以用户代码的错误是:它通过对行排序,改变了元素的相对位置,从而允许了不可能的顺序。 在原始行[10,1,10],元素1在中间,不能先;必须先一个端。 但在用户排序后,行变成[1,10,10],然后按索引,相当于先1(行首),但实际中不能先1。 所以代码是错误的。 在样例中,它工作了,因为样例中行尾或行首有较小值,但一般情况不成立。 例如,输入样例是[1,2,3]和[3,4,2],排序后[1,2,3]和[2,3,4],实际时,可以按此顺序(通过选择行首或行尾),但在[10,1,10]中,不能先1。 因此,正确方法是使用动态规划对每行独立求解,因为每行独立。 标准解法: - 对于每一行,计算该行的最大得分,使用dp。 - 然后总得分是各行的和。 dp定义:设f[i][j]表示当前行剩余区间[i,j]时的最大得分,t = m - (j-i) ?如前所述。 实现时,可以用区间长度循环。 但用户代码没有这样做。 所以错误是:用户代码错误地对行进行了排序,从而改变了元素的原始位置,导致顺序无效。 此外,在输入时,num[i][0]被访问,但未初始化,可能为0,这可能导致问题。例如,当j=1时,num[i][j-1]即num[i][0],是未定义的,可能为0。如果第一个元素是正,num[i][1] >0 > num[i][0]? 假设num[i][0]=0,则输入第一个元素时,if(num[i][1] <= num[i][0]) 即 if(a<=0),如果a>0,则false,不交换;但如果a=0,则true,但k循环可能出错,因为j=1,k从1到<1?k< j=1,k=1<1 false,所以不循环。但num[i][0]未初始化,是未定义行为,可能出错。 在代码中: ```cpp for(int j = 1;j <= m;j++){ cin >> num[i][j]; if(num[i][j] <= num[i][j - 1]){ // 当j=1,num[i][0]未初始化 // ... } } ``` num[i][0] 未被初始化,所以比较是未定义行为。 在样例中,可能侥幸工作,但应该避免。 总结错误: 1. 未初始化 num[i][0],导致未定义行为。 2. 错误地对行进行排序,改变了元素位置,使得后续计算基于错误序列。 3. 没有实现正确的策略(选择行首或行尾),而是假设按排序后顺序。 4. 在得分计算中,使用了列索引j作为,但实际依赖于全局顺序,不过由于每行独立,可以按行计算,但需要dp。 正确做法:对于每行,使用动态规划计算该行最大得分。 例如: ```cpp #include <iostream> #include <cstring> #include <algorithm> using namespace std; int n, m; int a[85]; long long dp[85][85]; // dp[l][r] for current row int main() { cin >> n >> m; long long total = 0; for (int i = 0; i < n; i++) { for (int j = 1; j <= m; j++) { cin >> a[j]; } memset(dp, 0, sizeof(dp)); // 初始化:单个元素 for (int j = 1; j <= m; j++) { // 当区间长度为1,是最后一次,第m次 dp[j][j] = a[j] * (1LL << m); // 2^m } // 区间长度从2到m for (int len = 2; len <= m; len++) { for (int l = 1; l + len - 1 <= m; l++) { int r = l + len - 1; int t = m - len + 1; // 当前是第t次,t从1到m,但这里len增加,t减少 // 实际上,t = m - (len - 1) ? 剩余元素len个,已 m - len 次,所以当前是第 (m - len + 1) 次 long long power = 1LL << t; // 2^t // 左端 long long left_val = a[l] * power + dp[l+1][r]; // 右端 long long right_val = a[r] * power + dp[l][r-1]; dp[l][r] = max(left_val, right_val); } } total += dp[1][m]; } cout << total << endl; return 0; } ``` 注意:2^m 可能很大,m<=80,2^80 ≈ 1e24,long long 可能不够(long long 约9e18),所以需要高精度或 __int128。 在题目中,对于60%据,答案不超过10^16,但100%据,m<=80,2^80=1.2e24,long long最大约9e18,所以可能溢出。 在NOIP中,可能需要高精度。 但在用户代码中,他用了long long sc,但在dp中必须处理大。 在样例中,82在long long范围内。 但用户代码没有用dp,所以错误。 所以用户代码的主要错误是排序和未初始化。 回答用户:你的代码有两个主要问题。首先,在输入元素时,你访问了未初始化的num[i][j-1],当j=1时,num[i][0]未初始化,导致未定义行为。其次,你试图对每一行进行排序,但题目要求时只能行首或行尾,排序改变了元素的原始位置,使得你计算的得分序列无效(例如,中间元素可能被错误地移到行首)。正确的方法是使用动态规划对每行独立求解最大得分。 然后,根据规则,我需要提出3-5个相关问题。 相关问题: 1. 为什么在矩阵游戏中使用动态规划? 2. 如何优化动态规划以避免大溢出? 3. 如果矩阵的行和列都影响顺序,如何修改算法? 4. 在动态规划中,状态转移方程是什么? 5. 如何处理2的幂次方导致的大问题? 确保输出格式。</think>你的代码存在两个主要问题: 1. **未初始化访问错误**:在输入循环中,当 `j=1` 时,`num[i][j-1]` 访问了未初始化的 `num[i][0]`(内存中可能是任意值)。这会导致条件判断 `if(num[i][j] <= num[i][j - 1])` 结果不可预测,可能引发错误的交换操作(如将有效据与垃圾值比较)。 2. **算法逻辑错误**: - 你试图通过插入排序将每行变为非递减序列,但题目要求**只能当前行首或行尾元素**。排序改变了元素的原始位置(如将 `[10,1,10]` 排序为 `[1,10,10]`),导致后续按索引顺序计算得分时,模拟了无效的顺序(如先中间元素)。 - 正确做法是使用动态规划(DP)独立处理每行: - 定义 `dp[l][r]` 表示行中剩余区间 `[l, r]` 时的最大得分。 - 状态转移:`dp[l][r] = max(a[l] * 2^t + dp[l+1][r], a[r] * 2^t + dp[l][r-1])`,其中 `t = m - (r-l)` 是当前。 - 初始化:当 `l=r` 时,`dp[l][r] = a[l] * 2^m`。 ### 修改建议(使用DP的代码框架): ```cpp #include <iostream> #include <cstring> #include <algorithm> using namespace std; int n, m; int a[85]; long long dp[85][85]; // DP int main() { cin >> n >> m; long long total = 0; for (int row = 0; row < n; row++) { for (int j = 1; j <= m; j++) cin >> a[j]; memset(dp, 0, sizeof(dp)); // 初始化:单个元素 for (int i = 1; i <= m; i++) { dp[i][i] = a[i] * (1LL << m); // 2^m } // DP:按区间长度递推 for (int len = 2; len <= m; len++) { for (int l = 1; l + len - 1 <= m; l++) { int r = l + len - 1; int t = m - (r - l); // 当前 t long long power = 1LL << t; // 2^t dp[l][r] = max(a[l] * power + dp[l+1][r], a[r] * power + dp[l][r-1]); } } total += dp[1][m]; } cout << total << endl; return 0; } ``` > **注意**:当 `m > 60` 时,`2^m` 可能超过 `long long` 范围(需用高精度或 `__int128`)。题目据 `m <= 80`,建议使用高精度处理(如用 `vector` 存储大)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值