KEYENCE Programming Contest 2021 CDE

KEYENCE Programming Contest 2021 CDE

C Robot on Grid 计数DP

题意

给定(H imes W)的网格,(k)个格子是有(R,D,X)的,剩下的格子则为空。

分别表示只能向右或者下或者任意或者可以自行填。

因此一共有(3^{H imes W - k})种填法,问所有填发的能够走到点((H,W))的方案数。

[2leq H,Wleq 5000\ 0leq K leq min(H imes W,2 imes 10^5) ]

分析

考虑(dp)(dp[i][j][k])表示到达((i,j))经历(k)个空的方案数。

[ans = sum dp[H][W][k] cdot 3^{HW-K - k} cdot2^k ]

2是因为当在路径时,要么填(X),要么填对应的方向。

显然直接(DP)复杂度是无法接受的。

考虑化简

[ans = 3^{HW-K}sum dp[H][W][k]cdot (frac{2}{3})^{k} ]

这样只需要转移的时候,没经过一次空,就乘上(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})

并且输出每次分割的方案

[1leq N leq 8 ]

分析

参考 https://www.cnblogs.com/Grice/p/14289881.html

对于一个序列而言,有(2 imes C_{2^N-1}^{2})对相同,((2^{N - 1})^2)对不同

假设选了(len)个序列,则要求

[len cdot 2 imes C_{2^N-1}^{2} = n cdot C_{2^N}^2\ len cdot (2^{N - 1})^2 = m cdot C_{2^N}^2 ]

可得到

[n : m = 2^{N - 1} - 1: 2^{N-1} ]

得到

[len = 2^N - 1 ]

于是只需要构造出解。

由于(N)比较小,于是从(1)开始递推找规律即可

E Greedy Ant 区间DP

题意

(N)个糖果排成一排,放在(2,4...2 imes N)号位,蚂蚁可以从奇数位开始吃糖果。

Sunke和蚂蚁一起吃,Sunke先手,并且每次可以选择任意一个糖果,蚂蚁只可以选择离它最近的糖果,若相同近则选择甜度最大的。

问Sunke能够选择的最大甜度

[1leq Nleq400\ 1leq a_ileq10^6\ a_i quad are quad distinct ]

分析

考虑状态设计,若直接考虑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) << '
';
} 
原文地址:https://www.cnblogs.com/hznumqf/p/14390113.html