LuoguP1005

LuoguP1005

九月 05, 2021

LG1005

十年以前,提高组的题目居然要写高精度!!!!

这不是摆明了刁难人吗!!!!!!!

Analysis

你需要写的高精度:

  • 高精度加法
  • 高精度比较大小
  • 高精度乘高精度
  • 高精度乘低精度
  • 高精度赋初值

虽然这一道题目是一道$dp$,但是高精度完全抢走了所有的风头

关于着一道题目的$dp$,首先,就是在于每一行都是独立的,互相影响,所以说每一行单独来$dp$就可以了。

我们设$f[i][j]$来表示$[i,j]$区间里面的最优情况,他可能是是从$[i+1,j]$和$[i,j-1]$两种情况推过来的,所以说因此就可以得出他的方程了

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include<bits/stdc++.h>
#define maxn 85
#define carry 1000
using namespace std;

int N,M;
int matrix[maxn][maxn];
struct Largenum{
int val[maxn];
int operator [](const int &ref)const{
return val[ref];
}

Largenum(int a){
memset(val,0,sizeof(val));
int pos=1;
while(a){
val[pos]=a%carry;
a/=carry;
pos++;
}
val[0]=pos;
}

Largenum(void){
memset(val,0,sizeof(val));
}

inline void output(){
printf("%d",val[val[0]]);
for(int i=val[0]-1;i>=1;i--)
printf("%03d",val[i]);
//大家都懂的,我这里是每一位存了一个三位数
}
Largenum operator +(const Largenum &obj)const{
Largenum cmp;
int add=0,pos=max(val[0],obj.val[0]);
for(int i=1;i<=pos;i++){
cmp.val[i]=obj.val[i]+val[i]+add;
add=cmp.val[i]/carry;
cmp.val[i]%=carry;
}
if(add>0)cmp.val[++pos]=add;
cmp.val[0]=pos;
return cmp;
}

bool operator ==(const Largenum &obj)const{
if(val[0]!=obj[0])return false;
for(int i=1;i<=val[0];i++)if(val[i]!=obj[i])return false;
return true;
}

bool operator <(const Largenum &obj)const{
if(val[0]>obj[0])return false;
if((*this)==obj)return false;
if(val[0]<obj[0])return true;
for(int i=val[0];i>=1;i--){
if(val[i]>obj[i])return false;
if(val[i]<obj[i])return true;
}
return true;
} //这个是状态转移比较大小用的

Largenum operator *(const Largenum &obj)const{
Largenum cmp;
int pos=val[0]+obj[0];
for(int i=1;i<=val[0];i++){
for(int j=1;j<=obj[0];j++){
cmp.val[i+j-1]+=val[i]*obj[j];
cmp.val[i+j]+=cmp.val[i+j-1]/carry;
cmp.val[i+j-1]=cmp.val[i+j-1]%carry;
}
}
while(cmp.val[pos]==0&&pos>1)pos--;
cmp.val[0]=pos;
return cmp;
}

Largenum operator *(const int &obj)const{
Largenum cmp;
int pos=val[0];int add=0;
for(int i=1;i<=pos;i++){
cmp.val[i]=val[i]*obj+add;
add=cmp.val[i]/carry;
cmp.val[i]=cmp.val[i]%carry;
}
while(add>0){
cmp.val[++pos]=add%carry;
add/=carry;
}
while(cmp.val[pos]==0&&pos>1)pos--;
cmp.val[0]=pos;
return cmp;
}
}two[maxn],f[maxn][maxn],ans(0);

void init(int M){
two[0]=Largenum(1);
for(int i=1;i<=M;i++)
two[i]=two[i-1]*2;//预处理一下
}

int main(){
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
scanf("%d",&matrix[i][j]);
//f[i][j]表示选择[i,j]区间时的最大值,它从f[i+1][j]和f[i][j+1]这两步枚举过来
init(M);
for(int k=1;k<=N;k++){
memset(f,0,sizeof(f));
for(int t=0;t<M;t++){
for(int i=1;i+t<=M;++i){
int j=i+t;
int pw=M-t;
//因为f[i][j]的定义,所以说这里是从短到长的来推的
//短的时候呢,乘的那一个数也要大一点
Largenum fir=(f[i+1][j]+two[pw]*matrix[k][i]);
Largenum sec=(f[i][j-1]+two[pw]*matrix[k][j]);
if(fir<sec)f[i][j]=sec;
else f[i][j]=fir;
}
ans=ans+f[1][M];
}
}
ans.output();
return 0;
}