这个题的大致思路是用搜索找出所有可能的邮票面值种数,在进行动态规划。
动态规划很简单,dp[i]表示的是组成价值为i的最小邮票数,那么很简单的就可以得到动态转移方程:dp[i]=min(dp[i],dp[i-a[j]]+1),a[j]是对邮票的面值进行枚举。那么,显然难点在于搜索上了。
我最开始的想法是,先把K种面值搜索出来。首先,1肯定是要的。但是搜索的上界并不容易确定。一个明显的一点是,最后一个邮票面值肯定不能超过前K-1种邮票面值的连续的邮资最大值加一。这样才能保证连续性,所以我写了下面25分的代码。
#include<iostream>
using namespace std;
int a[17], n, k, ans[17], maxn;
int min(int a, int b) {
return a < b ? a : b;
}
int dp() {
int f[50000];
f[0] = 0;
for (int i = 1; i <= a[k] * n; i++)
f[i] = 50000;
for (int i = 1; i <= k; i++) {
for (int j = a[i]; j <= a[k] * n; j++) {
f[j] = min(f[j], f[j - a[i]] + 1);
}
}
for (int i = 1; i <= a[k] * n; i++)
if (f[i] > n)
return i - 1;
return a[k] * n;
}
void DFS(int t) {
if (t == k + 1) {
int c = dp();
if (c > maxn) {
maxn = c;
for (int i = 1; i <= k; i++)
ans[i] = a[i];
}
return;
}
for (int i = a[t - 1] + 1; i <= maxn + 1; i++) {
a[t] = i;
DFS(t + 1);
}
}
int main()
{
cin >> n >> k;
for (int i = 1; i <= k; i++)
a[i] = i;
maxn = dp();
DFS(1);
for (int i = 1; i <= k; i++)
cout << ans[i] << " ";
cout << endl;
cout << "MAX=" << maxn << endl;
return 0;
}
这个错误的原因在于,搜索的上界太大,导致超时。所以要采用一种优化的方法。这种方法超时的原因在于对于每一种邮票面值的枚举都到达了上界,这样肯定会搜索一大堆不可能的情况。我们的任务就是减少这种情况。
因为我们对最后一个邮票的枚举上界的确定是前K-1种邮票面值的连续的邮资最大值加一。那么,我们可以考虑对于前j种邮票,第j+1种邮票的上界就是前j种邮票面值的连续的邮资最大值加一。这样,我们就可以每搜索一个邮票面值,就进行一次动态规划判断其最大值。
AC代码:
#include<iostream>
using namespace std;
int a[17], n, k, ans[17], maxn;
int min(int a, int b) {
return a < b ? a : b;
}
int dp(int t) {
int f[50000];
f[0] = 0;
for (int i = 1; i <= a[t] * n; i++)
f[i] = 50000;
for (int i = 1; i <= t; i++) {
for (int j = a[i]; j <= a[t] * n; j++) {
f[j] = min(f[j], f[j - a[i]] + 1);
}
}
for (int i = 1; i <= a[t] * n; i++)
if (f[i] > n)
return i - 1;
return a[t] * n;
}
void DFS(int t, int maxx) {
if (t == k + 1) {
if (maxx > maxn) {
maxn = maxx;
for (int i = 1; i <= k; i++)
ans[i] = a[i];
}
return;
}
for (int i = a[t - 1] + 1; i <= maxx + 1; i++) {
a[t] = i;
int x = dp(t);
DFS(t + 1, x);
}
}
int main()
{
cin >> n >> k;
DFS(1, 0);
for (int i = 1; i <= k; i++)
cout << ans[i] << " ";
cout << endl;
cout << "MAX=" << maxn << endl;
return 0;
}