题目链接:http://codeforces.com/contest/792/problem/C
题意:给了一个字符串,问能否删除一些数字使得这个数能被3整除,输出任意一个删除次数最小的剩下的数字串的合法解。
解法:Dp。dp[l]][zero][mod]代表第l位,是否有前导0,对3取模的最少删除次数
可以发现状态数最多100000*2*3,所以大力记忆化搜索,还要在搜索的过程中记录一下那些是被删除了。还有个坑了的地方就是特判0。
//CF 792C
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 100005;
int dp[maxn][2][3]; ///dp[i][j][k]代表第i位,是否有前导0,对3取模的最少删除次数
int path[maxn][2][3]; ///记录路径
int n, cnt, ans[maxn];
string s;
int dfs(int l, int zero, int mod)
{
if(~dp[l][zero][mod]) return dp[l][zero][mod];
if(l == n){
if(zero&&!mod) return 0;
else return -inf;
}
int x, y = -inf;
x = dfs(l+1, zero, mod);
path[l][zero][mod] = 0;
if(s[l] != '0' || (s[l] == '0' && zero)){
y = dfs(l+1, 1, (mod+(s[l]-'0')%3)%3) + 1;
if(y > x){
path[l][zero][mod] = 1;
return dp[l][zero][mod] = y;
}
}
return dp[l][zero][mod] = x;
}
void print_path(int l, int zero, int mod)
{
if(l == n) return;
if(path[l][zero][mod]){
ans[cnt++] = l;
print_path(l+1, 1, (mod+(s[l]-'0')%3)%3);
}else{
print_path(l+1, zero, mod);
}
}
int main()
{
cin >> s;
n = s.size();
memset(dp, -1, sizeof(dp));
int answer = dfs(0, false, 0);
if(answer <= 0){
bool flag = 0;
for(int i = 0; i < n; i++){
if(s[i] == '0'){
flag = 1;
break;
}
}
if(flag){
puts("0");
}
else{
puts("-1");
}
}
else{
cnt = 0;
memset(ans, 0, sizeof(ans));
print_path(0, false, 0);
for(int i = 0; i < cnt; i++){
putchar(s[ans[i]]);
}
printf("\n");
}
return 0;
}