如何使用动态规划自顶向下方法解决这个 problem?
Posted
技术标签:
【中文标题】如何使用动态规划自顶向下方法解决这个 problem?【英文标题】:How do I solve this problem using Dynamic Programming Top Down approach? 【发布时间】:2015-05-17 15:23:26 【问题描述】:我正在尝试解决 Codeforces 的问题 (http://codeforces.com/problemset/problem/189/A) 这是问题陈述:
Polycarpus 有一条丝带,它的长度是 n。他希望以满足以下两个条件的方式剪彩:
切割后,每条丝带的长度应为 a、b 或 c。 切割后的色带片数应为最大。 帮助 Polycarpus 并找到所需的丝带片数 切割。
输入 第一行包含四个空格分隔的整数 n、a、b 和 c (1 ≤ n, a, b, c ≤ 4000) — 相应的原始色带长度和切割后色带片的可接受长度。数字a、b和c可以重合。
输出 打印单个数字 — 最大可能的色带片数。保证至少存在一个正确的剪彩。
示例输入
5 5 3 2
样本输出
2
我尝试使用动态编程(自上而下的方法)来解决这个问题。但我无法得到正确的答案。递归函数可能有问题。这是我的代码:
#include<bits/stdc++.h>
using namespace std;
int n,s;
int a[3];
int val,m=-1;
int dp(int n)
if(n==0)
return 0;
for(int i=0;i<3;i++)
if(n>=a[i])
val=1+dp(n-a[i]);
if(val>m)
m=val;
return m;
int main()
scanf("%d %d %d %d",&n,&a[0],&a[1],&a[2]);
cout<<dp(n)<<endl;
return 0;
上述方法有什么问题?
【问题讨论】:
首先,您通常不应该包含<bits/*>
头文件,尤其是<bits/stdc++.h
,除非您确实需要该文件包含的所有头文件。 仅包含您需要的头文件,在您的情况下为<cstdio>
和<iostream>
。在 C++ 程序中使用scanf
有什么原因吗?
@JoachimPileborg 我知道在 C++ 代码中使用 有几个问题:
错误的搜索
在你的台词中
for(int i=0;i<3;i++)
if(n>=a[i])
val=1+dp(n-a[i]);
if(val>m)
m=val;
您应该检查针对i
的不同选择获得的不同val
s 的最大值。
错误终止
如果长度不为 0 且无法切割丝带,则应返回类似负无穷大的值。您当前返回的 m
最初为 -1(稍后会详细介绍)。这是错误的,对于长色带,基本上可以确保您只选择 a、b 和 c 中的最小值。
全局变量的使用
一些全局变量,例如 m
被初始化一次,但被递归修改。这不仅仅是“糟糕的”编程习惯——它没有做你想做的事。
不可重复使用
通过无条件调用递归,而不是重用以前的调用,你的运行时间会不必要地长。
【讨论】:
我使用 (n>=a[i]) 来防止函数 dp 具有负参数。因为 dp(一些负数) 没有用。这是我的假设。我是不是哪里错了? @ScottWu 确实,根据问题的陈述,cut 总是存在的,但是您仍然有列表中的第一个和第三个问题,并且可能还有时间限制【参考方案2】:int main()
int n, a, b, c;
scanf("%d %d %d %d", &n, &cuts[0], &cuts[1], &cuts[2]);
sort(cuts, cuts + 3);
for (int i = 0; i <= n; i++)
max_cuts[i] = INT_MIN;
max_cuts[0] = 0;
max_cuts[cuts[0]] = 1;
max_cuts[cuts[1]] = 1;
max_cuts[cuts[2]] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 3; j++)
if (cuts[j] > i) break;
max_cuts[i] = max(max_cuts[i - cuts[j]] + 1, max_cuts[i]);
printf("%d\n", max_cuts[n]);
return 0;
【讨论】:
【参考方案3】:@Ami Tavory 正确地提出了递归方法的问题。也许我下面的解决方案可以帮助您更好地理解如何形成状态和检查边界:
int main()
int n, a, b, c;
cin >> n >> a >> b >> c;
const int l = n + 1;
int sum[l];
fill(sum, sum+l, INT_MIN);
sum[0] = 0;
for(int i=1; i<=n; i++)
if(i - a >= 0)
sum[i] = sum[i-a] + 1;
if(i - b >= 0 && sum[i-b] + 1 > sum[i])
sum[i] = sum[i-b] + 1;
if(i - c >= 0 && sum[i-c] + 1 > sum[i])
sum[i] = sum[i-c] + 1;
cout << sum[n] << endl;
return 0;
仅在每个 sum[i] 处,我们都在最大化削减的数量。因此,在 sum[i] 处,我们存储了 max(sum[i-a]+1, sum[i-b]+1, sum[i-c]+1)。 除此之外,只有绑定检查。
【讨论】:
【参考方案4】:你可以通过自上而下的方法解决这个问题。一个 dp 问题总是检查所有可能的情况,然后给我们最佳解决方案。所以这里是代码
#include<bits/stdc++.h>
using namespace std;
int a,b,c;
int DP[4001];
int solve(int n)
if(n == 0)return 0;
if(n<0) return INT_MIN;
if(DP[n] != -1)return DP[n];
else
DP[n] = max(1+solve(n-a),max(1+solve(n-b),1+solve(n-c)));
return DP[n];
int main()
int n,x;
cin>>n>>a>>b>>c;
for(int i = 0;i<4001;++i)
DP[i] = -1;
x = solve(n);
cout<<x;
【讨论】:
以上是关于如何使用动态规划自顶向下方法解决这个 problem?的主要内容,如果未能解决你的问题,请参考以下文章