Codeforces Round #748 (Div. 3) F. Red-Black Number (dp)
-
题意:有一长度为\(n\)的数组,现在需要将每个数字染成黑色或红色,然后从左往右,红色数字拼接在一起,黑色数字拼接在一起,得到的两个数能够分别被\(A\)和\(B\)整除,同时黑色和红色涂色数尽可能相近,输出涂色方案。
-
题解:数据范围比较小,考虑四维dp,设\(dp[i][j][a][b]\)表示当前红色涂了\(i\)个,黑色涂了\(j\)个,上一个涂红色的余数为\(a\),上一个涂黑色的余数为\(b\),假如现在选红色并且上一个状态存在,那么状态转移方程为:\(dp[i][j][(a*10+(s[i+j]-'0'))\mod A][b]=[1,a]\).\([1,a]\)表示当前涂红色,并且从余数\(a\)转移过来。黑色同理,最后找到\(|r-b|\)最小的位置再还原路径即可。
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} char s[N]; PII dp[50][50][50][50]; char ans[N]; int main() { int _; scanf("%d",&_); while(_--){ int n,A,B; me(dp,0,sizeof(dp)); scanf("%d %d %d",&n,&A,&B); getchar(); scanf("%s",s+1); dp[0][0][0][0]={1,0}; for(int i=0;i<=n;++i){ for(int j=0;i+j<=n;++j){ if(i+j==0) continue; for(int a=0;a<A;++a){ for(int b=0;b<B;++b){ if(i && dp[i-1][j][a][b].fi) dp[i][j][(a*10+(s[i+j]-'0'))%A][b]={1,a}; if(j && dp[i][j-1][a][b].fi) dp[i][j][a][(b*10+(s[i+j]-'0'))%B]={2,b}; } } } } int res=0; for(int i=1;i<n;++i){ if(!dp[i][n-i][0][0].fi) continue; if(abs(n-2*res)>=abs(n-2*i)) res=i; } if(!res){ puts("-1"); continue; } int a=0,b=0; int x=res,y=n-res; for(int i=n;i>=1;--i){ auto now=dp[x][y][a][b]; if(now.fi==1) ans[i]='R',a=now.se,x--; else ans[i]='B',b=now.se,y--; } for(int i=1;i<=n;++i) printf("%c",ans[i]); puts(""); } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮