[NOIP1999]邮票面值设计

题目链接:http://www.rqnoj.cn/Problem_112.html

题目题意很简单,想法也很简单,直接深搜加枚举,不过鉴于数据,得适当的优化下。在求解当一个数被加入的时候,要找出相应的产生的最大面值,开始的时候一直没思路,后来想到可以用动规来枚举,这样这个题目就OK了。用动规的时候求解对于给定的一个面值如何用最少的票凑出来,这样我们就可以很容易的计算出上限。如果直接枚举的话,状态之间就很难判断。

算法:用n元组x[1 ..n]  表示n种不同的邮票面值,约定按照从小到大排列。x[1] = 1 是唯一的选择,此时的最大连续邮资区间是[1..m]。接下来,x[2] 的取值可能范围是x[2..m+1],在一般情况下,已选定x[1,i-1],最大连续邮资区间是[1,r],接下来x[i]的取值可能范围是

[x[i-1]+1 , r+1 ] 。这样可用回溯法来解决连续邮资问题,用树表示解空间。关于上限r的计算可以采用我前面所说的动规方式来解,也可以用全排列生成解。不过需要注意对应的时间代价。

代码如下:

#include 
#include 
#include 

using namespace std ;

const int maxn =  430 ;

int num[10] ;//用于存放中间结果,以及枚举
int dp[maxn][10] ;//用于动规计算一给定邮票面值如何用最少的邮票凑出来
int best[10] ;//用于存放最优结果

void dfs(int , int  , int ) ;
int max_dp(int , int) ; 

int n ;
int m ;
int r ;

int main()
{
	//freopen("data.in" , "r" , stdin) ;
	
	scanf("%d%d" , &n , &m) ;
		
	num[0] = 0 ;	
	num[1] = 1 ;
	
	int  i ;
	
	r =  n ;
	
	for(i = 2 ; i <= n + 1 ; i ++)
	{
		dfs(i , 2 , n) ;
	}
	
	for(i = 1 ; i <= m ; i ++)
	{
		printf("%d " , best[i]) ;
	}

	printf("\n") ;

	printf("MAX=%d\n" , r) ;

	return 0 ;
}

void dfs(int x , int layer , int to)
{
	int  i ;
	int temp ;
	
	if(layer > m)
	{
		if(to >= r)//此处需要注意这个条件,在这里wrong了一次
		{
			r = to ;

			for(i = 1 ; i <= m ; i ++)
			{
				best[i] = num[i] ;
			}
		}
		return 	;
	}
		
	num[layer] = x ;	
	
	temp = max_dp(layer , to) ;
		
	for(i = num[layer] + 1 ; i <= temp + 1 ; i ++)
	{
		dfs(i , layer + 1 , temp ) ;
	}
}

int max_dp(int layer , int to)
{
	int i ;
	int j ;
	
	memset(dp , 0 , sizeof(dp)) ;

	i = 0 ;
	//利用动规求解上限r
	while( dp[i][layer] <= n )
	{
 		++i ;
 		
		for(j = 1 ; j <= layer ; j ++)
		{
			if(i >= num[j] && j > 1)

				dp[i][j] = dp[i][j-1] < (dp[i-num[j]][j] + 1) ? dp[i][j-1] : (dp[i-num[j]][j] + 1) ;

			else if(j > 1)

				dp[i][j] = dp[i][j-1] ;

			else if(i >= num[j])

				dp[i][j] = dp[i-num[j]][j] + 1 ;
		}
	}
	
	return --i ;
}


你可能感兴趣的:(动规,搜索)