【补题】Educational Codeforces Round 150 (Rated for Div. 2) C. Ranom Numbers
题意:给一串字符串,从A-E,数值大小分别为1,10,100,1000,10000,对于这个字符串数值总和的计算方式是,如果那一位的字符的右侧有严格大于它的字符,那么数值变为负数,有一次随意改写一个位置字符的操作,问最大的数值是多少。
dp思路:
1.首先由于题目的性质,改变一个位置的字符,不会对它的右侧造成影响,所以在已知右侧字符的情况下,左侧其实要关注的是已知字符子串的最大,因为大于最大不会受影响,小于怎么样都受影响,只需要关注已知出现的最大字符,这给了dp的机会,所以首先反转字符串
2.反转字符串在于,现在问题就转化成了,已知左边字符串的情况,问右边在后续的最大数值,因为前面不会受后面影响,所以这就能dp了。
那么已知左侧,其实只要关注最大,上面解释过了,dp数组此时很明显dp[i][j][k],代表当进行到第i位,出现过的最大字符(j),然后操作过k次(k被限定为0/1)即可,然后就是正常dp。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
const int N = 3e5+10;
const int INF = 1e18;
const int MOD = 1e9+7;
int cnum[5]={1,10,100,1000,10000};
int dp[N][5][2];
void solve(){
string s;
cin >> s;
s=" "+s;
reverse(s.begin()+1,s.end());
int n=s.size();
for(int i=0;i<=n;i++){
for(int j=0;j<5;j++){
for(int k=0;k<2;k++){
dp[i][j][k]=-INF;
}
}
}
dp[0][0][0]=0;
for(int i=1;i<n;i++){
for(int j=0;j<5;j++){
for(int k=0;k<2;k++){
if(j>(s[i]-'A')){
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k]-cnum[s[i]-'A']);
}
else{
dp[i][s[i]-'A'][k]=max(dp[i][s[i]-'A'][k],dp[i-1][j][k]+cnum[s[i]-'A']);
}
}
for(int k=0;k<5;k++){
if(j>k){
dp[i][j][1]=max(dp[i][j][1],dp[i-1][j][0]-cnum[k]);
}
else{
dp[i][k][1]=max(dp[i][k][1],dp[i-1][j][0]+cnum[k]);
}
}
}
}
int res=-INF;
for(int i=0;i<5;i++){
res=max(res,dp[n-1][i][0]);
res=max(res,dp[n-1][i][1]);
}
cout << res << endl;
}
signed main() {
IOS;
int t = 1;
cin >> t;
while (t--) {
solve();
}
}
非常简洁明了的代码,感觉比自己写的强了几百倍,自己写的时候不仅方向错误,而且后面看了思路,实现的也不好。