P1005 矩陣取數遊戲
阿新 • • 發佈:2020-08-29
P1005 矩陣取數遊戲
題目描述
帥帥經常跟同學玩一個矩陣取數遊戲:對於一個給定的 n×m 的矩陣,矩陣中的每個元素 ai,j 均為非負整數。遊戲規則如下:
- 每次取數時須從每行各取走一個元素,共 n 個。經過 m 次後取完矩陣內所有元素;
- 每次取走的各個元素只能是該元素所在行的行首或行尾;
- 每次取數都有一個得分值,為每行取數的得分之和,每行取數的得分 = 被取走的元素值 ×\(2^i\),其中 i 表示第 i 次取數(從 1 開始編號);
- 遊戲結束總得分為 m 次取數得分之和。
帥帥想請你幫忙寫一個程式,對於任意矩陣,可以求出取數後的最大得分。
輸入格式
輸入檔案包括 n+1 行:
第一行為兩個用空格隔開的整數 n 和 m。
第 2∽n+1 行為 n×m 矩陣,其中每行有 m 個用單個空格隔開的非負整數。
輸出格式
輸出檔案僅包含1行,為一個整數,即輸入矩陣取數後的最大得分。
輸入輸出樣例
輸入 #1
2 3
1 2 3
3 4 2
輸出 #1
82
資料範圍:
100% 的資料滿足:1≤n,m≤80,0≤ai,j≤1000。
一道區間DP題。
我們發現每行的答案都是分開來的,並沒有相互影響,我們就可以一行一行的找最大值,假設當前找到了第\(k\)行;
\(f[i][j]\)陣列表示\(i\)到\(j\)這段區間沒有選數的最大得分,那麼狀態轉移方程就很好列出來:
\[f[i][j] = max(f[i - 1][j] + 2^{m - j + i} * a[k][i - 1], f[i][j + 1] + 2^{m - j + i} * a[k][j + 1]) \]
注意\(i > j + 1\),這表示這一行找完了,沒有數可選了,如果只找到\(i = j\),那相當於少找了一個數。
這道題要用高精,不過用__int128可以水過去。
#include <iostream> #include <cstdio> #include <cctype> #include <cstring> using namespace std; inline __int128 read() { __int128 s = 0, f = 1; char ch; while(!isdigit(ch = getchar())) (ch == '-') && (f = -f); for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48)); return s * f; } int n, m; __int128 f[85][85], a[85][85]; __int128 ans; __int128 ksm(__int128 x, int y) { __int128 res = 1; while(y) { if(y & 1) res = res * x; x = x * x; y >>= 1; } return res; } void put(__int128 x) { if(x > 9) { put(x / 10); } printf("%d", (int) (x % 10)); } void init() { n = read(); m = read(); for(int i = 1;i <= n; i++) { for(int j = 1;j <= m; j++) { a[i][j] = read(); } } } void work() { for(int k = 1;k <= n; k++) { memset(f, 0, sizeof(f)); __int128 res = 0; for(int i = 1;i <= m; i++) { for(int j = m;j >= 1; j--) { if(i > j + 1) break; int num = m - j + i - 1; f[i][j] = max(f[i][j], f[i - 1][j] + ksm(2, num) * a[k][i - 1]); f[i][j] = max(f[i][j], f[i][j + 1] + ksm(2, num) * a[k][j + 1]); res = max(res, f[i][j]); } } ans += res; } put(ans); } int main() { init(); work(); return 0; }