Loading

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)\)的方案数。

\[2\leq H,W\leq 5000\\ 0\leq K \leq min(H\times W,2 \times 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 << '\n';
}

D Choosing Up Sides 组合 构造

题意

给出\(N\),表示\(2^N\)个人,要求经过最少次数的分割,使任意两个人最终有恰好\(n\)次在同个队伍,\(m\)次在不同的队伍

每次分割把\(2^n\)人分为每部分\(2^{N-1}\)

并且输出每次分割的方案

\[1\leq N \leq 8 \]

分析

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

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

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

\[len \cdot 2 \times 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\times N\)号位,蚂蚁可以从奇数位开始吃糖果。

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

问Sunke能够选择的最大甜度

\[1\leq N\leq400\\ 1\leq a_i\leq10^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) << '\n';
} 
posted @ 2021-02-08 19:07  MQFLLY  阅读(146)  评论(0编辑  收藏  举报