• 【带限制的完全背包】Educational Codeforces Round 133 (Rated for Div. 2) D. Chip Move


    题意:
    给定 n n n k k k,初始步长为 k k k ,每次可以走 k k k 的倍数次步,下一次走 ( k + 1 ) (k+1) (k+1)的倍数次步,以此类推,问从 0 0 0 开始到达 [ 1 , n ] [1,n] [1,n] 每个点的方案数。

    题解:
    当每次只走一倍的步数时, ( s t e p + k ) × ( s t e p − k + 1 ) 2 ≤ n \frac{(step+k)\times (step-k+1)}{2}\leq n 2(step+k)×(stepk+1)n,所以最多只会走 O ( n ) O(\sqrt{n}) O(n )次。

    问题转换为带限制的完全背包问题,走第 i i i 次前,必须已经走了 1 1 1 i − 1 i-1 i1

    f [ i ] [ j ] f[i][j] f[i][j] 表示走了 i i i次,第 i i i 次走完后到达点 j j j 的方案数
    i i i 次走的步数为 ( k + i − 1 ) (k+i-1) (k+i1) 的倍数

    不带限制的完全背包的模板为:

    for (int i = 1; i <= n; ++i) f[i] = 0;
    f[0] = 1;
    
    for (int step = k; (start = (step + k) * (step - k + 1) / 2) <= n; ++step)
    	for (int j = 0; j <= n; ++j)
    		if (j >= step) f[i][j] += f[i - 1][j - step];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是本题的限制为:走第 i i i 次前,必须已经走了 1 1 1 i − 1 i-1 i1
    每次需要强制走一下第 i − 1 i-1 i1 次,如下:

    for (int i = 1; i <= n; ++i) f[0][i] = 0;
    f[0][0] = 1;
    for (int step = k; (step + k) * (step - k + 1) / 2 <= n; ++step) {
    	for (int j = 0; j <= n; ++j)
    		if (j >= step) f[i][j] = f[i - 1][j - step];
    		else f[i][j] = 0;
    	for (int j = 0; j <= n; ++j)
    		if (j >= step) f[i][j] += f[i - 1][j - step];
    	for (int j = 1; j <= n; ++j) ans[j] += f[j];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    滚动数组优化一下:

    for (int i = 1; i <= n; ++i) f[i] = 0;
    f[0] = 1;
    
    for (int step = k; (step + k) * (step - k + 1) / 2 <= n; ++step) {
    	for (int j = n; j >= 0; --j)
    		if (j >= step) f[j] = f[j - step];
    		else f[j] = 0;
    		
    	for (int j = 0; j <= n; ++j)
    		if (j >= step) f[j] += f[j - step];
    	for (int j = 1; j <= n; ++j) ans[j] += f[j];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    代码:

    #include 
    using namespace std;
    
    typedef long long ll;
    
    const int N = 200010;
    const int mod = 998244353;
    
    int dp[2][N];
    int f[N];
    int ans[N];
    int n, k;
    
    void add(int& a, int b) {
    	a += b;
    	if (a >= mod) a -= mod;
    }
    
    void solve() {
    	scanf("%d%d", &n, &k);
    	for (int i = 1; i <= n; ++i) f[i] = 0;
    	f[0] = 1;
    	
    	for (int step = k, start; (start = (step + k) * (step - k + 1) / 2) <= n; ++step) {
    		for (int j = n; j >= 0; --j)
    			if (j >= start) f[j] = f[j - step];
    			else f[j] = 0;
    			
    		for (int j = start; j <= n; ++j)
    			if (j >= start) add(f[j], f[j - step]);
    		for (int j = 1; j <= n; ++j) add(ans[j], f[j]);
    	}
    	for (int i = 1; i <= n; ++i) printf("%d%c", ans[i], " \n"[i == n]); 
    }
    
    int main()
    {
    	int T = 1;
    //	scanf("%d", &T);
    	for (int i = 1; i <= T; ++i) {
    		solve();
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    总结:
    避免跳跃性思考,从朴素的二维状态构建好转移方程后,再根据实际优化空间和时间

  • 相关阅读:
    【机器学习】最大期望算法(EM)
    web前端面试题附答案044 - vue获取param参数,有什么缺点吗?
    存u盘里的视频没删除找不到了怎么办?别急,这几招来帮您
    c++语言--面试题
    stm32备份
    A1034 Head of a Gang(30分)PAT 甲级(Advanced Level) Practice(C++)满分题解【DFS+图的遍历】
    项目管理岗,HR和PMO青睐的点有哪些差异?
    VINS-Mono-后端优化 (三:视觉雅可比推导)
    MySQL之多表查询—列子查询
    向量组的秩、矩阵的秩
  • 原文地址:https://blog.csdn.net/weixin_43900869/article/details/126183703