KEYENCE Programming Contest 2021 CDE
KEYENCE Programming Contest 2021 CDE
C Robot on Grid 计数DP
题意
给定\(H \times W\)的网格,\(k\)个格子是有\(R,D,X\)的,剩下的格子则为空。
分别表示只能向右或者下或者任意或者可以自行填。
因此一共有\(3^{H \times W - k}\)种填法,问所有填发的能够走到点\((H,W)\)的方案数。
分析
考虑\(dp\),\(dp[i][j][k]\)表示到达\((i,j)\)经历\(k\)个空的方案数。
2是因为当在路径时,要么填\(X\),要么填对应的方向。
显然直接\(DP\)复杂度是无法接受的。
考虑化简
这样只需要转移的时候,没经过一次空,就乘上\(\frac{2}{3}\)即可
代码
int mp[maxn][maxn]; // X : 1 D:2 R:3
ll dp[maxn][maxn];
char s[5];
int main(){
int h = rd();
int w = rd();
int k = rd();
for(int i = 0;i < k;i++){
int x = rd();
int y = rd();
scanf("%s",s);
if(s[0] == 'X') mp[x][y] = 1;
else if(s[0] == 'D') mp[x][y] = 2;
else mp[x][y] = 3;
}
dp[1][1] = 1;
ll kk = ksm(3,MOD - 2,MOD);
for(int i = 1;i <= h;i++){
for(int j = 1;j <= w;j++){
if(mp[i][j] == 1 || mp[i][j] == 3) dp[i][j + 1] += dp[i][j],dp[i][j + 1] %= MOD;
if(mp[i][j] == 1 || mp[i][j] == 2) dp[i + 1][j] += dp[i][j],dp[i + 1][j] %= MOD;
if(!mp[i][j]) {
dp[i][j + 1] += dp[i][j] * 2 * kk % MOD,dp[i][j + 1] %= MOD;
dp[i + 1][j] += dp[i][j] * 2 * kk % MOD,dp[i + 1][j] %= MOD;
}
}
}
cout << ksm(3,h * w - k,MOD) * dp[h][w] % MOD << '\n';
}
D Choosing Up Sides 组合 构造
题意
给出\(N\),表示\(2^N\)个人,要求经过最少次数的分割,使任意两个人最终有恰好\(n\)次在同个队伍,\(m\)次在不同的队伍
每次分割把\(2^n\)人分为每部分\(2^{N-1}\)人
并且输出每次分割的方案
分析
参考 https://www.cnblogs.com/Grice/p/14289881.html
对于一个序列而言,有\(2 \times C_{2^N-1}^{2}\)对相同,\((2^{N - 1})^2\)对不同
假设选了\(len\)个序列,则要求
可得到
得到
于是只需要构造出解。
由于\(N\)比较小,于是从\(1\)开始递推找规律即可
E Greedy Ant 区间DP
题意
有\(N\)个糖果排成一排,放在\(2,4...2\times N\)号位,蚂蚁可以从奇数位开始吃糖果。
Sunke和蚂蚁一起吃,Sunke先手,并且每次可以选择任意一个糖果,蚂蚁只可以选择离它最近的糖果,若相同近则选择甜度最大的。
问Sunke能够选择的最大甜度
分析
考虑状态设计,若直接考虑Sunke的选取情况,则很不好记录到状态中。
注意到Sunke的选择不一定会影响到当前蚂蚁的决策,于是我们可以把选择次数先“保留下来”
\(dp[i][j][k]\)表示\([i,j]\)都已经选取了,Sunke还可以选择\(k\)次的最优解
于是转移
- \(k > 0\),Sunke可以选择\(l\)或者\(r\),用\(dp[l-1][r][k-1] + a_l\)或者\(dp[l][r+1][k-1] + a_r\)转移
- Sunke不选择,用\(dp[l-1][r][k+1]\)或者\(dp[l][r+1][k+1]\)转移
直接\(for\)好像不太好写,直接用记忆化搜索
代码
int n;
int a[maxn];
ll dp[maxn][maxn][maxn];
ll cal(int l,int r,int k){
if(dp[l][r][k] != -1) return dp[l][r][k];
ll ans = 0;
if(l == 0 && r == n + 1)
return dp[l][r][k] = 0;
if(l >= 1){
if(k) ans = max(ans,cal(l - 1,r,k - 1) + a[l]);
if(a[l] > a[r]) ans = max(ans,cal(l - 1,r,k + 1));
}
if(r <= n) {
if(k) ans = max(ans,cal(l,r + 1,k - 1) + a[r]);
if(a[l] < a[r]) ans = max(ans,cal(l,r + 1,k + 1));
}
return dp[l][r][k] = ans;
}
int main(){
n = rd();
for(int i = 1;i <= n;i++)
a[i] = rd();
memset(dp,-1,sizeof dp);
for(int i = 0;i <= n;i++)
cout << cal(i,i + 1,1) << '\n';
}