洛谷——P1021 邮票面值设计

19 篇文章 0 订阅
2 篇文章 0 订阅

这个题的大致思路是用搜索找出所有可能的邮票面值种数,在进行动态规划。
动态规划很简单,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值