jzxx_1174 [USACO] 麦香牛块

本文详细解析了经典的麦香牛肉问题,通过数学归纳法和动态规划的方法,解决了寻找最大不可购买牛肉数量的问题,并给出了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

农夫约翰的奶牛几乎要武装暴动,因为他们听说麦当劳要推出新产品麦香牛肉。奶牛们要尽力阻止这种产品的上市。他们研究了一种“劣等包装”策略。奶牛们说:“如果麦香牛肉有3块,6块以及10块装这三种,那么想买 1, 2, 4, 5, 7, 8, 11, 14, 或17块牛肉的顾客就得不到满足了。劣等的包装,劣等的产品!” 帮助奶牛们。给出N (不同包装的种类数, 1 <= N <= 10),以及N个正整数 (1 <= i <= 256)表示每种包装中牛肉数量,输出最大的不能买到的牛肉数量。如果任何消费要求都可以被满足或不能满足的牛肉数量没有上界,则输出0。最大的可能值(如果存在)不超过2,000,000,000。

输入

第1行: N第2..N+1行: 一个盒子里的牛肉数量。

输出

输出题目中要求的单个整数。

样例输入

3
3
6
10

样例输出

17


            当第一次看到这个题目的时候,一点想法都没有。数据范围那么大,如果一个个的枚举过去,时间上肯定会暴,而且也没有实际意义。题目要求的是最大的不能买到的数量,也就是在题目提供的数据以任何方式组合都不能达到的最大数。我当时在考虑,怎么保证某一个很大数是可以通过题目提供的数据来组合。

       我当时想着用一个数组存储提供数据的所有组合,但显然这样做不仅麻烦而且是错误的。

       于是,在网上看了别人的说明:这是一道基本的DP题目。

       // 如果输入的数据里面有 1  肯定就没有上界了;

      //  如果输入的数据的最大公约数不为1,则没有上界;

      //  数据最大 不会超过 max*max

       有些点到目前还不是很理解,在此记录下来,以后慢慢再进行研究。以下是网上找到的数学证明:


由于数目非常庞大,不能简单进行动态规划,必须先进行数学分析缩小范围。此题进行数学抽象,给定n个正整数{an},于是有集合S={a1*x1+a2*x2+...+an*xn,xi属于N},求集合N-S 的属性,以下先从n=2 开始阐述。

 

引理1:S={a*x + b*y, x,y属于自然数N},当a、b 互素时,N-S为有限集,其最大值为 a*b-a-b;当a、b不互素时(a,b最大公约数大于1),则N-S 为无限集

 

证明:

(1) 当a、b不互素时(a,b最大公约数大于1),则N-S 为无限集

这个比较好证明,设p=gcd(a,b),则a*x+b*y必定可以写成 p*z 的形式,由于p>1,则p* z + 1必定不属于S,于是N-S 为无限集

 

(2) 当a、b互素时,则N-S 为有限集,且最大值为a*b-a-b

首先证明 a*b-a-b 不属于S,使用反证法,

a*b-a-b = a*x + b*y  => a*(b-1-x) = b*(1+y),由于 a、b互素,则有b-1-x>=b,这与定义矛盾,于是有a*b-a-b 必定不属于S

 

然后证明任何一个数z > a*b-a-b,均能表示为a*x + b*y(x,y属于N),令z=a*b-a-b+i,因为a、b互素,则i=a*m+b*n(m,n属于整数,不是自然数),于是z=a*(b-1+m) + b*(n-1),当m、n均不为负数时,z均可以表示为a*x+b*y的形式,下面证明m、n有一个为负数时的情况。

不失一般性,设i=a*x-b*y(x,y属于N),我们可以做一个变换,确保y<=a-1,当y>=a时,设y=a*r+s(s&lt;=a-1),则有i=a*x-b*(a*r+s) =a(x-br)-bs,即i表示为a*x-b*y时,可以确保y<=a-1,此时必有x>=1。

z=a*b-a-b+a*x-b*y = a(x-1) + b(a-1-y),由于 x>=1,y<=a-1,所以z可以表示,命题得证。

此命题表明S 包含大于等于ab-a-b+1的所有整数

 

引理2:S={a*x + b*y + c*z, x,y,z属于自然数N},当a、b、c互素时,N-S为有限集,其最大值不大于MAX(ab,ac,bc)

 

证明

如果其中有两个元素互素,比如gcd(a,b)=1,则由引理1得到N-S为有限集,其最大值必定不大于ab-a-b,即不大于MAX(ab,ac,bc)

 

假设任意两个元素均不互素,不失一般性,令ab=MAX(ab,ac,bc),p=gcd(a,b),于是有a*x+b*y+c*z=p*(a1*x+b1*y) + c*z,其中p和z互素,a1和b1互素,将a1*x+b1*y看作一个元素,运用引理1得到 N-S 的最大值为pc-p-c,但由于a1*x+b1*y从a1b1-a1-b1之后才开始连续,所以N-S的最大值必定小于pc-p-c+ p(a1b1-a1-b1) < pc + ab/p

只要证明pc + ab/p <ab即可,因为p>=2则ab/p<=ab/2,因为p<=a/2,c<b,则pc<ab/2,于是命题得证。

引理2的证明给我们提供了解决原问题的思路

 

 

定理:S={a1*x1+a2*x2+...+an*xn,xi属于N},当a1i,a2,...,an互素时,N-S的最大值小于MAX(ai) * MAX(ai)

 

使用数学归纳法证明,n=2,3时已经证明,假设n-1时已经证明,以下证明n时

如果其中有n-1个互素,则问题直接得证,即以下的证明为任意n-1个元素不互素的情况。不失一般性,假设n个数中a1和a2为最大的两个数,设p=gcd(a1,a2,...,an-1)

a1*x1+a2*x2+...+an*xn= p(a’1*x1+...+a’n-1*xn-1) + an*xn

根据归纳假设a’1*x1+...+a’n-1*xn-1至少从a’1a’2开始连续,且运用引理1,可以得到N-S最大值小于p an -p- an  +p(a’1 a’2) < p an+ (a1 a2)/2,利用与引理2相同的证明方法可以得到N-S的最大值小于a1a2

根据此定理,可以将原麦香牛块问题的范围限制到256*256之中,再使用动态规划求解就可行了。


原文地址:http://blog.youkuaiyun.com/wdq347/article/details/9313699


根据网上代码改的代码如下:

#include <stdio.h>

#define MAXN 70000      // 256*256
#define MAX(a,b) ((a) > (b) ? (a) : (b))

int GCD(int a, int b){
	if(a % b)
		return GCD(b, a%b);
	else 
		return b;
}    // 最大公约数

int main(){
	int f[MAXN];
	int num[20], n, m;
	int i, j;
	scanf("%d", &n);

	for(i = 1; i <= n; ++i){
		scanf("%d", &num[i]);
		if(num[i] == 1){              // 如果输入有1 则没有上界
			printf("0\n");
			return 0;
		}
	}
	
	int k = num[1];
	int max = num[1];
	for(i = 2; i <= n; ++i){
		k = GCD(k, num[i]);        //输入数据的最大公约数
		max = MAX(max, num[i]);     //输入数据中的最大数
	}
	if(k != 1){                       // 最大公约数>1 , 则么有上界
		printf("0\n");
		return 0;
	}
	
	m = max * max;                // 上限
	f[0] = 1;
	
	for(i = 1; i <= n; ++i){
		for(j = num[i]; j <= m; ++j){         //如果f[j] 可以放一盒牛块,则置1 ;当i=1的时候
			if (f[j - num[i]])            //能用a[1]=3 组合的数据都已经置1了,然后再组合上
				f[j] = 1;            //a[2]=6 的所有数据,以此类推,就是所有的数据了。
		}
	}
	
	while(f[m]) --m;                    //倒推
	printf("%d\n", m);	
}

其实网上还有另一种解法是顺着来的,即先进行判定,如果在某一个体积里面刚好放进一盒牛块,则置其为1。就好像在一长条有格子的盒子里面放牛肉一样,以f[0]为基准状态,从a[1]=3 开始一直往后面放,即f[0]+k*a[1] (k是自然数),这样,在有限的情况下,3的倍数就放满了,然后是 a[2] = 6, 也是从f[0]基准状态开始,以此类推。这样的到

f[k] = 1 是在 t*a1 + p*a2 + q*a3 (t, p, q 是自然数),符合了题目的要求。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值