KEYENCE Programming Contest 2021 CDE
C Robot on Grid 计数DP
题意
给定(H imes W)的网格,(k)个格子是有(R,D,X)的,剩下的格子则为空。
分别表示只能向右或者下或者任意或者可以自行填。
因此一共有(3^{H imes 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 << '
';
}
D Choosing Up Sides 组合 构造
题意
给出(N),表示(2^N)个人,要求经过最少次数的分割,使任意两个人最终有恰好(n)次在同个队伍,(m)次在不同的队伍
每次分割把(2^n)人分为每部分(2^{N-1})人
并且输出每次分割的方案
分析
参考 https://www.cnblogs.com/Grice/p/14289881.html
对于一个序列而言,有(2 imes C_{2^N-1}^{2})对相同,((2^{N - 1})^2)对不同
假设选了(len)个序列,则要求
可得到
得到
于是只需要构造出解。
由于(N)比较小,于是从(1)开始递推找规律即可
E Greedy Ant 区间DP
题意
有(N)个糖果排成一排,放在(2,4...2 imes 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) << '
';
}