动态规划: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; }