P1021 [NOIP1999 提高组] 邮票面值设计 题解

回溯搜索,DP

  1. 首先分析得出邮票面值中,肯定有1分
  2. 用a数组记录邮票面值,在DP中用b数组记录每种邮票面值所用的次数,x数组用于记录所更新的最优解
  3. 初值a[1]=1;
  4. 回溯
  • 在js()函数中得到a数组中i-1种邮票面值连续的邮资最大值res
  • 判断是否能更新
  • 枚举从a[i-1]+1到res的邮票面值
void find(int i)
{
	int k,z;
	z=js(i-1);	
	if (i>n)
	{
		if (z-1>ans) 
		{
			ans=z-1;
			for (int j=1; j<=n; j++) x[j]=a[j];
		}
		return;	
	}
	for (k=z; k>=a[i-1]+1; k--)
	{
		a[i]=k;
		find(i+1);	
	}
}
  1. DP
  • b数组初始化,赋上最大值
  • a数组中第i种邮票面值的邮资数为1
  • 从res=0开始枚举
  • b[res]=min(b[res-a[i]]+1,b[res])
  • 当b[res]超过最多能粘贴的邮票数时,退出循环
int js(int t)
{
   int i,res;
   for (i=1; i<=1000; i++) b[i]=1000000000;
   for (i=1; i<=t; i++) b[a[i]]=1;
   res=0;
   do
   {
   	res++;
   	for (i=1; i<=t; i++)
   		if (res>a[i] && b[res-a[i]]+1<b[res])
   			b[res]=b[res-a[i]]+1;
   }while (b[res]<=m);
   return res;
}

最后,附上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
int m,n,ans,a[1005],b[1005],x[1005];

 int js(int t)
{
	int i,res;
	for (i=1; i<=1000; i++) b[i]=1000000000;
	for (i=1; i<=t; i++) b[a[i]]=1;
	res=0;
	do
	{
		res++;
		for (i=1; i<=t; i++)
			if (res>a[i] && b[res-a[i]]+1<b[res])
				b[res]=b[res-a[i]]+1;
	}while (b[res]<=m);
	return res;
}

 void find(int i)
{
	int k,z;
	z=js(i-1);	
	if (i>n)
	{
		if (z-1>ans) 
		{
			ans=z-1;
			for (int j=1; j<=n; j++) x[j]=a[j];
		}
		return;	
	}
	for (k=z; k>=a[i-1]+1; k--)
	{
		a[i]=k;
		find(i+1);	
	}
}

 int main()
{
	scanf("%d%d",&m,&n);
	a[1]=1;
	find(2);	
	for (int i=1; i<=n; i++) printf("%d ",x[i]); printf("\n");
	printf("MAX=%d\n",ans);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值