• 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) << '
    ';
    } 
    
  • 相关阅读:
    python js(JavaScript)初识
    python css盒子型 浮动
    12、液化
    11、高斯模糊—晶格化
    10、形状工具—渐变图标
    9、形状工具—雪糕
    8、形状工具—制作简单立体效果
    7、形状工具
    6、自由变换—再复制功能
    第三章-17、消息聊天界面
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14390113.html
Copyright © 2020-2023  润新知