https://codeforces.com/problemset/problem/792/C
思路:
开始dp[i][0~2][0~1]:表示当前mod为0~2且该数删还是不删。然后自己做的时候发现前导零没有处理掉。同时这样输出方案有些麻烦。
所以压最后一个维。
对于前导零,如果当前数字为0并且前面没有有效答案,就跳过此状态,由初始值的未更新来代替。
对于输出答案,本题有一点灵活。记录当前状态由哪个最优状态转移过来。回溯方案的时候判断当前数不删除的话代价是否仍然是当前状态的最小。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef int LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
LL dp[maxn][3];
LL pre[maxn][3];
char str[maxn];
LL a[maxn];
int main(void){
cin.tie(0);std::ios::sync_with_stdio(false);
cin>>(str+1);LL n=strlen(str+1);
for(LL i=1;i<=n;i++) a[i]=(str[i]-'0');
if(n==1){
if(a[1]%3) cout<<"-1"<<"\n";
else cout<<a[1]<<"\n";
return 0;
}
memset(dp,0x3f,sizeof(dp));
memset(pre,-1,sizeof(pre));
dp[1][0]=1;
dp[1][a[1]%3]=0;
for(LL i=2;i<=n;i++){
LL x=a[i]%3;
for(LL j=0;j<3;j++){
dp[i][j]=dp[i-1][j]+1;///删除
pre[i][j]=j;
if(a[i]==0&&dp[i-1][j]==i-1) continue;///前导零
LL temp=dp[i][j];
dp[i][j]=min(dp[i][j],dp[i-1][(j-x+3)%3]);///保留
if(temp>dp[i][j]){
pre[i][j]=(j-x+3)%3;
}
}
}
if(dp[n][0]==n){
for(LL i=1;i<=n;i++) if(a[i]==0){cout<<"0"<<"\n";return 0;}///如果前面有0
cout<<"-1"<<"\n";return 0;
}
vector<LL>ans;
LL last;
for(LL i=n,j=0;i>=2;j=last,i--){
last=pre[i][j];
if(dp[i-1][last]==dp[i][j]) ans.push_back(a[i]);
}
if(a[1]%3==last) ans.push_back(a[1]);
reverse(ans.begin(),ans.end());
for(auto i:ans){
cout<<i;
}
cout<<"\n";
return 0;
}