动态规划进阶题目之货币面值

题目描述

小虎是游戏中的一个国王,在他管理的国家中发行了很多不同面额的纸币,用这
些纸币进行任意的组合可以在游戏中购买各种装备来提升自己。有一天,他突然
很想知道这些纸币的组合不能表示的最小面额是多少,请聪明的你来帮助小虎来
解决这个财政问题吧。
输入
输入包含多个测试用例,每组测试用例的第一行输入一个整数N(N<=100)表示流
通的纸币面额数量,第二行是N个纸币的具体表示面额,取值[1,100]。
输出
对于每组测试用例,输出一个整数,表示已经发行的所有纸币都不能表示的最小
面额(已经发行的每个纸币面额最多只能使用一次,但面值可能有重复)。
样例输入
5
1 2 3 9 100
5
1 2 4 9 100
5
1 2 4 7 100
样例输出
7
8
15
提示
注意一下给出的货币面值可能不是升序排列的。

题目分析

这是一个变形的01背包问题,使得物品充满背包,这个题中面值不仅仅是面值(物品的价值),还是背包的大小
用s[i]表示每种面值的大小,f[i][j]表示前i种币种在最大总面额为j的情况下达到的最大总面值,n是币种的数量,s[i]是每个币种的面值,num是所有的币种面值之和
递推公式(状态转移方程):
if(j>=s[i])
f[i][j]=max(f[i-1][j-s[i]]+s[i],f[i-1][j]);
else
f[i][j]=f[i-1][j];

临界条件:f[0][j]=0;
答案就是j从1到num依次判断f[n][j]!=j,如否是,j即为答案
代码如下:

#include(二维数组)
#include
#include
#define max(x,y) x>y?x:y;
int s[50]={0};
int main()
{
	int n;
 	int sum=0;
 	int i=0,j;
 	int f[100][200];//f[i][0]=0
 	while(scanf("%d",&n)!=EOF)
	 {
 		 sum=0;
             	 memset(f,0,sizeof(int*));
 		 for(i=1;i<=n;i++)
 		 {
  			 scanf("%d",&s[i]);
  			 sum+=s[i];
  			 f[i][0]=0;
  		}
  		for(j=0;j<=sum;j++)
   			f[0][j]=0;
  		for(i=1;i<=n;i++)
  		{
  			 for(j=1;j<=sum;j++)
   			{
				    if(j>=s[i])
				     f[i][j]=max(f[i-1][j-s[i]]+s[i],f[i-1][j])
				    else
				    f[i][j]=f[i-1][j];
		        }
		}
		 for(j=1;j<=sum;j++)
 		 {
   			 if(f[n][j]!=j)
 			  {
   				 printf("%d\n",j);
   				 break;
   			  }
                }
	 }
	 return 0;
}

空间优化

通过递推公式(状态转移方程)可以看出
f[i[[j]之于f[i-1][]有关,与i-2,i-3,i-4…无关,所以可以只设定一个一维数组f[j]
通过不断的更新最终获得结果
优化代码:

#include
#include
#define max(x,y) x>y?x:y;
int main()
{
   int n;
   int sum=0;
   int i=0,j;
   int s[50];
   int f[200];
   while(scanf("%d",&n)!=EOF)
   {
      sum=0;
      for(i=1;i<=n;i++)
      {
         scanf("%d",&s[i]);
		 sum+=s[i];
     }
	 memset(f,0,sizeof(f));
     for(i=1;i<=n;i++)
	 {
	    for(j=sum;j>=1;j--)
        {
		   if(j>=s[i])
	   	   f[j]=max(f[j-s[i]]+s[i],f[j]);
		}
      }
	  for(j=1;j<=sum;j++)
      {
	     if(f[j]!=j)
		 {
		    printf("%d\n",j);
				break;
         }
      }
   }
   return 0;
}

你可能感兴趣的:(练习题目)