【做题纪要】CodeForces 动态规划专题“在天亮之前,看饿狼成群狩猎,一遍 一遍 吞噬箴言”

CF1253E

题目大意:

你现在有 \(n\) 个天线,第 \(i\) 个天线的位置为 \(x_i\),每个天线的覆盖范围是 \([x_i-s_i,x_i+s_i]\),每次操作都可以花费 \(1\) 的代价使覆盖范围 \(s_i\) 增加 \(i\) 问最少花费多少代价让 \([1,m]\) 被全部覆盖

做法:

\(f(i,j)\) 表示覆盖到 \(i,i\) 被第 \(j\) 个覆盖缩需要的最小代价。然后分成两种情况

如果 \(a_j >i\),枚举之前使用的天线 \(k\),有转移 \(f(i,j)←f(i−1,k)+a_j−i−s_j\),把天线 \(j\) 操作到可以覆盖当前的位置。

如果 \(a_j \le i\) 那就不太友善了,因为往后拓展的时候可能会多覆盖到前面,花费会变得更小,所以得从当前点关于 a_j 的对称点转移过来。即 \(f(i,j)←f(2a_j−i−1,k)+i−a_j−s_i\)

然后就可以直接做了,复杂度 \(O(n^2m)\)

我想不通 这不同 为什么
int n,m,a[maxn],s[maxn],f[100005][105];
signed main(){
    n=read(),m=read();
    for_(i,1,n) a[i]=read(),s[i]=read();
    memset(f, 0x3f, sizeof f);
    for_(i,1,m)
        for_(j,1,n)
            if (a[j] <= i){
                for_(k,1,n){
                    int w = max(i - a[j] - s[j], 0ll);
                    if (2 * a[j] - i - 1 > 0) w += f[2 * a[j] - i - 1][k];
                    f[i][j] = min(f[i][j], w);	            
                    if (i - a[j] < a[j])
                        f[i][j] = min(f[i][j], max(0ll, a[j]-s[j]-1));
                }
            }
            else {
                if (i != 1) 
					f[i][j] = min(f[i][j], f[i-1][j]);
                for_(k,1,n){
                    f[i][j] = min(f[i][j],((i-1)?f[i-1][k]:0)+max(a[j]-i-s[j],0ll));
                }
            }
    int ans = inf;
	for_(i,1,n){
		ans = min(ans,f[m][i]);
	}
    writeln(ans);
}

CF392B Tower of Hanoi

\(f_{i,j,k}\) 表示把前 k 个盘子从 i 号柱移动到 j 号柱上的最少花费

\(f_{i,j,k}=\min(f_{i,6−i−j,k−1}+a_{i,j}+f_{6−i−j,j,k−1},f_{i,j,k−1}×2+a_{i,6−i−j}+f_{j,i,k−1}+a_{6−i−j,j})\)

分隔了你我 让选择 演变成漩涡
int T[4][4],n,f[100][4][4];
inline int dfs(int k,int s,int t){
	if (s==t) return 0;	
	if (f[k][s][t]) return f[k][s][t];
	if (k==n)
	  return min(T[s][t],T[s][6-s-t]+T[6-s-t][t]);
	return f[k][s][t]=min(dfs(k+1,s,6-s-t)+T[s][t]+dfs(k+1,6-s-t,t),(dfs(k+1,s,t)*2)+dfs(k+1,t,s)+T[s][6-s-t]+T[6-s-t][t]);
}
signed main(){
	for_(i,1,3)
	  for_(j,1,3) 
	    T[i][j]=read();
	n=read();
	writeln(dfs(1,1,3));
}

CF425C Sereja and Two Sequences

我去,这个题怎么硬控我半天,最后居然发现是水题,设计 \(f_{i,j}\) 表示第一个序列删到第 \(i\) 个数,操作了 \(j\) 次,第二个序列最小删到第 \(f_{i,j}\) 个数。

首先 \(f_{i,j}=f_{i−1,j}\) 是显然的,接考虑从一个点 \(f_{k,j−1}\) 转移到 \(f_{i,j}\),即为在 \(f_{k,j−1}\) 这个位置后再找到第一个 \(b_{x}=a_i,f_{i,j}\) 即为 \(x\)

CF449D Jzzhu and Numbers

\(f_s\) 表示子集含 \(s\) 的方案数,倒着处理一遍所有含 \(s\)\(a\) 的个数,那么显然是 \(f_s=2^{num_s}−1\),直接计算即可

CF305E Playing with String

这题题解写了两三遍全死机了。。。一次都没存

首先刚看到题面我们就有一个很简单的 O(n^3) 的 dp 计算区间 SG 函数的做法,但是哇塞,我们的 |S| 足足有 5000,所以 O(n^3) 做法是过不去的说,那就只能优化一下了,只要优化到 O(n^2) 就能做了

我们把 s_{i-1}=s_{i+1} 的点称作特殊点,那么我们就能发现两个相邻的特殊点删掉一个就会导致另一个也被删除,那么我们只需要提取出所有连续的特殊点一段一段做即可,容易发现不被特殊点连接的特殊点不会相互影响,此时我们求 SG 函数的 dp 就被转化为了一维的 dp,复杂度 O(n^2) 可以通过

CF731E Funny Game

这怎么还有 Furry Game,不管了

看完题面极度显然这是一个博弈 dp,首先对这个序列求出前缀和。

因为双方均想最大化自己的得分,对于每一步,都会考虑前缀和与最大值的差和最大值那个更大,其中更大的就是我们的答案

posted @ 2025-02-24 15:02  Vsinger_洛天依  阅读(58)  评论(2)    收藏  举报