洛谷P1021题解

原题

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


思路概述

题意分析

给定两个整数 \(N,K(N+K≤15)\),设计 \(K\) 种邮票面值\(\{a_1,a_2\dots a_K\}\),并用共 \(N\) 张上述邮票表示出连续面值 \(\{1,2,3\dots MAX\}\)。要求设计出的邮票能表示出的连续面值 \(MAX\) 最大。最后输出设计的邮票面值 \(\{a_i|1≤i≤K\}\) 与能表示出的最大连续面值 \(MAX\)

思路简述

邮票面值只能靠深搜(原因后文阐述)。

由于需要表示出连续的面值,所以需要更大面值的情况是当且仅当当前种类的邮票无法表示出该面值,故可以按递增顺序选取面值。

对于已经选取出的邮票,则需要计算出其能表示出的最大面值。此处可以用01背包的思路解决:

\[\text{令}f_x\text{表示表示出面值}x\text{所需要的邮票数(下同)} \]

\[rec_i\text{表示第}i\text{张邮票的面值(下同)} \]

\[\text{则有}f_i=\min \{f_{i-rec_j}+1\},j∈[1,k] \]


算法实现

关于深搜的正确性

首先可以知道一条显而易见的结论:\(a_1=1\)

然后可以分析得知的是,并非邮票面值设置越大,表示的最大面值就越大,例如对于数据 \(N=3,K=2\),若选取邮票面值 \(\{1,4\}\),则只能表示面值范围 \([1,6]\);而取 \(\{1,3\}\) 时,则可以表示面值 \([1,7]\)。因此,为确保正确性,又考虑到数据规模较小,本题采用深搜查找邮票面值组合。

关于深搜策略与范围

由于是需要表示出最大的面值,所以在当前选取的邮票面值可以表示的情况下,不轻易再选取更大的面值。相应地,选取更大面值的条件就是当前选取的面值在 \(N\) 张的数量限制内不能表示目前考虑的面值,即 \(f_i>n\)

考虑一种极端情况(尽管这种情况不可能出现),若当前的最大面值取到 \(MAX\),那么\(n\) 个面值为 \(MAX\) 的邮票加在一起只能表示面值 \(n·MAX\),而面值 \(n·MAX+1\) 则需要选取更大面值的邮票。故 \(MAX'∈[MAX+1,n·MAX+1]\)


AC code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
const int maxn=3e2+10;
int n,k,maximum;
int f[maxn],rec[maxn],ans[maxn];
inline int calc(int pos);
inline void dfs(int pos);
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin >> n >> k;
	rec[1]=1;dfs(2);
	for(RI i=1;i<=k;++i) printf("%d ",ans[i]);printf("\nMAX=%d",maximum);
	return 0;
}
inline int calc(int pos)
{
	for(RI i=1;i<=maxn-1;++i) f[i]=0x3f3f3f3f;
	for(RI i=1;i<=maxn-1;++i)
	{
		for(RI j=1;j<=pos && i-rec[j]>=0;++j)
			if(f[i-rec[j]]<n) f[i]=min(f[i],f[i-rec[j]]+1);
		if(f[i]==0x3f3f3f3f) return i-1;
	}
}
inline void dfs(int pos)
{
	if(pos>k) 
	{
		RI temp=calc(k);
		if(temp>maximum)
		{
			maximum=temp;
			for(RI i=1;i<=k;++i) ans[i]=rec[i];
		}
	}
	else
	{
		for(RI i=1;i<=rec[pos-1]*n;++i)
		{
			rec[pos]=rec[pos-1]+i;
			dfs(pos+1);
		}
	}
	return;
}

posted @ 2022-10-26 21:55  UOB  阅读(157)  评论(0)    收藏  举报