3. 矩阵取数游戏
(game.pas/c/cpp)
【问题描述】
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n*m 的矩阵,矩阵中的每个元素 aij 均
为非负整数。游戏规则如下:
- 每次取数时须从每行各取走一个元素,共 n 个。m 次后取完矩阵所有元素;
- 每次取走的各个元素只能是该元素所在行的行首或行尾;
- 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2i,其中 i 表示第 i 次取数(从 1 开始编号);
- 游戏结束总得分为 m 次取数得分之和。
帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
【输入】
输入文件game.in包括 n+1 行:
第 1 行为两个用空格隔开的整数 n 和 m。
第 2~n+1 行为 n*m 矩阵,其中每行有 m 个用单个空格隔开的非负整数。
【输出】
输出文件game.out仅包含 1 行,为一个整数,即输入矩阵取数后的最大得分。
【输入输出样例1】
game.in |
game.out |
2 3 1 2 3 3 4 2 |
82 |
【输入输出样例1解释】
第 1 次:第 1 行取行首元素,第 2 行取行尾元素,本次得分为 1*21+2*21=6
第 2 次:两行均取行首元素,本次得分为 2*22+3*22=20
3 3
第 3 次:得分为 3*2 +4*2 =56。总得分为 6+20+56=82
【输入输出样例2】
game.in |
game.out |
1 4 4 5 0 5 |
122 |
【输入输出样例3】
game.in |
game.out |
2 10 96 56 54 46 86 12 23 88 80 43 16 95 18 29 30 53 88 83 64 67 |
316994 |
【限制】
60%的数据满足:1<=n, m<=30, 答案不超过 1016
100%的数据满足:1<=n, m<=80, 0<=aij<=1000
【思路】
区间DP+高精度
可以从题目中看出每一行的选取都是独立的,而每一行的最优解共同构成答案,因此对n行分别求解。
对一行而言,状态转移方程:
D[i][j]=max{d[i+1][j]+sq[tmp]*A[i], d[i][j-1]+A[j]*sq[tmp] };
(tmp=n+i-j , sq[i]定义为2^i)
就数据来看我们需要用到高精度。
【代码】
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 6 using namespace std; 7 8 typedef long long LL; 9 const int maxn= 80+5; 10 const int MAXN = 410; 11 12 struct bign 13 { 14 int len, s[MAXN]; 15 bign () 16 { 17 memset(s, 0, sizeof(s)); 18 len = 1; 19 } 20 bign (int num) { *this = num; } 21 bign (const char *num) { *this = num; } 22 bign operator = (const int num) 23 { 24 char s[MAXN]; 25 sprintf(s, "%d", num); 26 *this = s; 27 return *this; 28 } 29 bign operator = (const char *num) 30 { 31 for(int i = 0; num[i] == '0'; num++) ; //è¥?°μ?0 32 len = strlen(num); 33 for(int i = 0; i < len; i++) s[i] = num[len-i-1] - '0'; 34 return *this; 35 } 36 bign operator + (const bign &b) const //+ 37 { 38 bign c; 39 c.len = 0; 40 for(int i = 0, g = 0; g || i < max(len, b.len); i++) 41 { 42 int x = g; 43 if(i < len) x += s[i]; 44 if(i < b.len) x += b.s[i]; 45 c.s[c.len++] = x % 10; 46 g = x / 10; 47 } 48 return c; 49 } 50 bign operator += (const bign &b) 51 { 52 *this = *this + b; 53 return *this; 54 } 55 void clean() 56 { 57 while(len > 1 && !s[len-1]) len--; 58 } 59 bign operator * (const bign &b) //* 60 { 61 bign c; 62 c.len = len + b.len; 63 for(int i = 0; i < len; i++) 64 { 65 for(int j = 0; j < b.len; j++) 66 { 67 c.s[i+j] += s[i] * b.s[j]; 68 } 69 } 70 for(int i = 0; i < c.len; i++) 71 { 72 c.s[i+1] += c.s[i]/10; 73 c.s[i] %= 10; 74 } 75 c.clean(); 76 return c; 77 } 78 bign operator *= (const bign &b) 79 { 80 *this = *this * b; 81 return *this; 82 } 83 bign operator - (const bign &b) 84 { 85 bign c; 86 c.len = 0; 87 for(int i = 0, g = 0; i < len; i++) 88 { 89 int x = s[i] - g; 90 if(i < b.len) x -= b.s[i]; 91 if(x >= 0) g = 0; 92 else 93 { 94 g = 1; 95 x += 10; 96 } 97 c.s[c.len++] = x; 98 } 99 c.clean(); 100 return c; 101 } 102 bign operator -= (const bign &b) 103 { 104 *this = *this - b; 105 return *this; 106 } 107 bign operator / (const bign &b) 108 { 109 bign c, f = 0; 110 for(int i = len-1; i >= 0; i--) 111 { 112 f = f*10; 113 f.s[0] = s[i]; 114 while(f >= b) 115 { 116 f -= b; 117 c.s[i]++; 118 } 119 } 120 c.len = len; 121 c.clean(); 122 return c; 123 } 124 bign operator /= (const bign &b) 125 { 126 *this = *this / b; 127 return *this; 128 } 129 bign operator % (const bign &b) 130 { 131 bign r = *this / b; 132 r = *this - r*b; 133 return r; 134 } 135 bign operator %= (const bign &b) 136 { 137 *this = *this % b; 138 return *this; 139 } 140 bool operator < (const bign &b) 141 { 142 if(len != b.len) return len < b.len; 143 for(int i = len-1; i >= 0; i--) 144 { 145 if(s[i] != b.s[i]) return s[i] < b.s[i]; 146 } 147 return false; 148 } 149 bool operator > (const bign &b) 150 { 151 if(len != b.len) return len > b.len; 152 for(int i = len-1; i >= 0; i--) 153 { 154 if(s[i] != b.s[i]) return s[i] > b.s[i]; 155 } 156 return false; 157 } 158 bool operator == (const bign &b) 159 { 160 return !(*this > b) && !(*this < b); 161 } 162 bool operator != (const bign &b) 163 { 164 return !(*this == b); 165 } 166 bool operator <= (const bign &b) 167 { 168 return *this < b || *this == b; 169 } 170 bool operator >= (const bign &b) 171 { 172 return *this > b || *this == b; 173 } 174 string str() const 175 { 176 string res = ""; 177 for(int i = 0; i < len; i++) res = char(s[i]+'0') + res; 178 return res; 179 } 180 }; 181 182 istream& operator >> (istream &in, bign &x) 183 { 184 string s; 185 in >> s; 186 x = s.c_str(); 187 return in; 188 } 189 190 ostream& operator << (ostream &out, const bign &x) 191 { 192 out << x.str(); 193 return out; 194 } 195 196 bign d[maxn][maxn]; 197 bign A[maxn][maxn]; 198 bign sq[maxn]; 199 bign ans=0; 200 int n,m; 201 202 int main() { 203 ios::sync_with_stdio(false); 204 cin>>n>>m; 205 FOR(i,1,n+1) FOR(j,1,m+1) cin>>A[i][j]; 206 sq[0]=1; FOR(i,1,m+1){ sq[i]=sq[i-1]*2;} //高精单精乘 207 FOR(T,1,n+1){ 208 FOR(l,0,m) FOR(i,1,m-l+1) { //m 209 int j=i+l , tmp=m+i-j; 210 if(l==0) { d[i][i]=A[T][i]*sq[tmp];continue; } 211 bign v1=d[i+1][j]+A[T][i]*sq[tmp] ,v2= d[i][j-1]+A[T][j]*sq[tmp] ; 212 if(v1>v2) d[i][j]=v1; else d[i][j]=v2; //高精赋值 213 } 214 ans = ans + d[1][m]; //高精高精加 215 } 216 cout<<ans; 217 return 0; 218 }