P1005 [NOIP2007 提高組] 矩陣取數遊戲
阿新 • • 發佈:2021-11-03
演算法要素:奇怪的區間dp+高精度int128暴打高精
思路分析:
很容易想到每行之間根本沒有任何關係。
因此問題轉化為了:在長度為m的區間中從區間兩端取數\(a[i]\),第k次得分為\(a[i]\times x\)。
要求使每行最終總得分最大。最終答案為所有行的最大得分之和。
具體實現:
(1)這題的一大特點就是細節特別多。
可設出\(dp\)式 \(dp[g][i][j]\) 表示在第g行中\((i+1,j-1)\) 為當前未選擇的數的區間。
轉移式則為:
答案為:
(2)細節:
\(i\)在\(j\)的外層列舉,\(i\)從\(0\)開始遞增列舉,\(j\)從m+1開始遞減列舉。
要求\(j\)大於\(i\)。
(3)資料範圍:
\(n,m<=80\)
那麼答案的最大值估算為\(2^{80}\times1000\),而\(int128\)的範圍是\(2^{128}\)。
欸嘿這就水過去了。
這個\(dp\)式的\(i\)、\(j\)的意義和範圍我都調了好久。。。
Code:
#include<bits/stdc++.h> using namespace std; const int maxn=105; __int128 dp[maxn][maxn][maxn],a[maxn][maxn]; int n,m; __int128 qpow(__int128 x,__int128 y) { __int128 res=1; while(y) { if(y&1) res=res*x; x=x*x; y>>=1; } return res; } __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; } void write(__int128 x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) a[i][j]=read(); for(int i=1;i<=n;++i) { dp[i][0][m]=a[i][m]*2; dp[i][1][m+1]=a[i][1]*2; } for(int g=1;g<=n;++g) { for(int i=0;i<=m;++i) { for(int j=m+1;j>i;--j) { if(i>=1) dp[g][i][j]=max(dp[g][i][j],dp[g][i-1][j]+qpow(2,i+m-j+1)*a[g][i]); if(j<=m) dp[g][i][j]=max(dp[g][i][j],dp[g][i][j+1]+qpow(2,i+m-j+1)*a[g][j]); } } } __int128 ans=0; for(int i=1;i<=n;++i) { __int128 sum=0; for(int j=0;j<=m;++j) { sum=max(sum,dp[i][j][j+1]); } ans+=sum; } write(ans); return 0; }