usaco Beef McNuggets

这两天贼烦,ccf炸了,还有一个烦心事。哎我都不知道自己能不能坚持下去了。马上期末考了。这段时间还是抓紧时间复习吧同时刷usaco的节奏要跟以前一样了,毕竟课少了。



题解:

只要你知道以下的数论结论,这道题就是水背包了:

有两个数p,q,且gcd(q,p)=1,则最大无法表示成px+qy(x>=0,y>=0)的数是pq-q-p(对于n>pq-q-p,都可以表示成px+qy;而pq-q-p,就无法表示成px+qy)。

而且数越多,这个值只会越小。

所以我们只需考虑小于pq-q-p的范围的最小值。对于一些无解的(全体最大公约数>1),或无数解的(有一个‘1’),应提前判断。 其实我们可以干脆全取上界为256*256-256*2

分析:第一步:判断这 n 个数的gcd,如果  gcd!= 1 则无解;
第二步:如果 gcd=1 则解小于最大的两个数的LCM。
第三步:背包,使用标记数组 d[],d[i]=d[i-a[j]];
简单证明一下:
第一步:
a1*x1+a2*x2+……+an*xn = p;
a1*y1+a2*y2+……+an*yn = p+1;
所以:a1*(y1-x1)+a2*(y2-x2)+……+an*(yn-xn) = 1;
所以:gcd(a1,a2,……,an)*[a1/gcd(a1,a2,……,an)*(y1-x1) + a2/gcd(a1,a2,……,an)*(y2-x2) +……+ an/gcd(a1,a2,……,an)*(yn-xn)]=1(a)
所以:gcd(a1,a2,……,an)=1;
第二步:……
以上证明就是证明红字部分要让大于这个数所有的数都可以表示明显p可以,则p+1肯定可以。得到a式。用反证法证明一下即可知gcd(a1-an)=1;

[a1/gcd(a1,a2,……,an)*(y1-x1) + a2/gcd(a1,a2,……,an)*(y2-x2) +……+ an/gcd(a1,a2,……,an)*(yn-xn)]=1/gcd(a1,a2,……,an);
因为等式左边是整数等式右边也是整数所以只能等于1;
然后就是dp找到所有状态。从大到小看那个状态不存在。
/*
ID:jinbo wu
LANG:C++
TASK: nuggets
*/ 
#include<bits/stdc++.h>
using namespace std;
int dp[256*256];
int a[15];
int gcd(int b,int c)
{
	if(c==0)
	return b;
	return gcd(c,b%c);
}
bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
	freopen("nuggets.in","r",stdin);
	freopen("nuggets.out","w",stdout);
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
	cin>>a[i];
	int g=a[0];
	if(n==1)
	{
		cout<<"0"<<endl;
		return 0;
	}
	for(int i=1;i<n;i++)
	{
		g=gcd(g,a[i]);
	    if(g==1)
	    break;
	}
	if(g!=1)
	{
		cout<<"0"<<endl;
		return 0;
	}
	sort(a,a+n,cmp);
	dp[0]=1;
	int sum=a[0]*a[1]-a[0]-a[1];
	for(int i=0;i<n;i++)
	{
		for(int j=a[i];j<=sum;j++)
		{
			if(dp[j-a[i]])
			dp[j]=1;
		}
	}
	for(int i=sum;i>=0;i--)
	{
		if(!dp[i])
		{
			cout<<i<<endl;
			return 0;
		}
	 } 
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值