Red-Black Number

思路:我们发现每个格子有两种染色方式,所以一共是2402^{40}种,不能通过,想到可以进行折半搜索,每次2202^{20}种,然后再进行整合答案,能够通过。但有一种规模更小的搜索方法。我们设f(i,j,k,t)f(i,j,k,t)表示当前进行到第ii个,当前被染红的格子的十进制表示modAmodA 的值为jj,染黑的格子的十进制表示modBmodB的值为kk,当前有tt个红格子的状态是否可行。

考虑搜索,即

f(i,j,k,t)>f(i+1,(j10+s[i+1])moda,k,t+1)f(i,j,k,t) ->f(i+1,(j*10+s[i+1])moda,k,t+1)

f(i,j,k,t)>f(i+1,j,(k10+s[i+1])modb,t)f(i,j,k,t)->f(i+1,j,(k*10+s[i+1])modb,t)

然后统计一下答案即可。

Code:Code:

const int N = 33+10;

char s[N];
int _;
int a,b,n;
int ans = 99999;
bool st[N][N][N][N];
//int head[N][N][N][N];
vector<int>G,Ans;
void dfs(int id,int j,int k,int t){
    if(st[id][j][k][t] == 1)return;
    if(id == n and j == 0 and k == 0 and t != 0 and t != n){
        int now = abs(t - (n - t));
        if(ans > now){
            Ans.clear();Ans = G;
            //for(auto i:Ans)printf("%d",i);pc('\n');
            ans = now;return ;
        }
    }
    
    st[id][j][k][t] = 1;
    if(id == n)return ;
    int num = s[id+1] - '0';
    int x = (num + j * 10)%a;
    int y = (num + k * 10)%b;
    //head[id+1][x][k][t+1] = 
    G.pb(id+1);
    dfs(id+1,x,k,t+1);
    G.pop_back();
    dfs(id+1,j,y,t);
}
bool col[N];
void solve(){
    read(_);
    while(_--){
        ans = 99999;
        memset(st,false,sizeof st);
        memset(col,false,sizeof col);
        G.clear();
        read(n);read(a);read(b);
        scanf("%s",s+1);
        dfs(0,0,0,0);
        //write(ans);pc('\n');
        if(ans == 99999){puts("-1"); }
        else {
            for(auto i:Ans)col[i] = true;
            for(int i=1;i<=n;++i)pc(col[i]?'R':'B');
            pc('\n');
        }
    }
}

这道题告诉我,当我们面对这种搜索规模偏大的情况下,我们可以考虑运用记忆化搜索 + 设定变换维度 ,来达到缩小搜索规模的目的。当动态规划方程不太直观时,我们可以转为运用记忆化搜索,从前向后更新状态,这样在某些题中更为直观自然。