USACO4.1.1--Beef McNuggets
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USACO4.1.1--Beef McNuggets相关的知识,希望对你有一定的参考价值。
chunlvxiong的博客
题目描述:
有N种包装盒(1≤N≤10),每种包装盒可以包装牛块i块(1≤i≤256),每种包装盒可以使用任意次。求无法包装的最大的牛块数,若所有数目的牛块都能被包装,或者无法包装的最大牛快数为oo,那么输出0。
思考&分析:
其实这是个数论问题--给定N个数,求不能由这些数通过加减得到的最大数。
如果只有两个数,那么这个问题比较经典,也存在如下结论:若gcd(p,q)==1 ,则px+qy不能表示的最大数为pq-p-q。
如果N个数中没有任何两个数的GCD==1的话,那么最大的不能得到的数一定是oo,因为这N个数必然存在一个非1公因子X,从而使组成的数均为X的倍数,%X!=0的数将无法组成。
除去这种情况,答案最大只有256^2,那么使用DP解决即可:用dp[i][j]表示前i个数能否表示j,方程如下:
dp[i][j]=dp[i][j] or dp[i-1][j-k*num[i]](k>=0 && j>=k*num[i])
这里利用完全背包的思想进行优化,可以把方程改为:
dp[j]=dp[j] or dp[j-num[i]](j>=num[i],同时继承时从小到大继承)
这样时间复杂度O(N*256^2),空间复杂度O(256^2)-->可以顺利AC此题。
其实我更想谈的是另一种想法:
设这N个数的最小值为T,则如果X能被组成的话,X+KT也能被组成(K>=0),所以你可以认为要求%T=X的最小的数。
这是一个最短路问题:把%T==0/1/2……T-1看成T个点,每个点都会连出N条边(X-->(X+A[i])%T,权值为A[i]),共N*T条边,并不多,可以跑SPFA解决。
接下来对于结果(dis数组)有以下几种情况:
dis[i]==i表示%T==i的所有数都能被组成-->如果所有的dis[i]==i表示所有数都能被组成,输出0。
dis[i]==oo表示%T==i的所有数都不能被组成-->表示最大不能被组成的数是oo,输出0。
dis[i]=X表示%T==i的最小能被组成的数是X-->%T==i的最大不能被组成的数是X-T,取MAX即可。
这样就可以得到结果了,如果用SPFA写时间复杂度O(T*N*T)(远不会到这个上限),用dijkstra写时间复杂度O(T*T),加堆优化可以达到O((T+N*T)log(N*T)),可以解决规模更大的问题。
贴代码:
数论+DP:
#include<bits/stdc++.h> using namespace std; const int Max=256*256; int n,a[15],dp[Max+5]; int gcd(int a,int b){ if (!b) return a; else return gcd(b,a%b); } bool check(){ for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (gcd(a[i],a[j])==1) return 0; return 1; } int main(){ freopen("nuggets.in","r",stdin); freopen("nuggets.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); if (check()){ puts("0"); return 0; } memset(dp,0,sizeof(dp)); dp[0]=1; for (int i=1;i<=n;i++) for (int j=a[i];j<=Max;j++) dp[j]|=dp[j-a[i]]; int ans=0; for (int i=Max;i>=1;i--) if (!dp[i]){ ans=i; break; } printf("%d\\n",ans); return 0; }
最短路:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll oo=1e13; const int maxn=305; const int maxm=3005; int head[maxn],Q[maxn],tot; ll dis[maxn]; int T,n,a[15],front,rear; bool vis[maxn]; struct E{ int to,len,next; }edge[maxm]; void init(){ memset(head,0,sizeof(head)),tot=0; } void makedge(int u,int v,int t){ edge[++tot].to=v; edge[tot].len=t; edge[tot].next=head[u]; head[u]=tot; } void push(int x){ vis[x]=1; Q[rear]=x; rear=(rear+1)%maxn; } void spfa(){ int u,v; memset(dis,1,sizeof(dis)); memset(vis,0,sizeof(vis)); front=rear=dis[0]=0,push(0); while (front!=rear){ u=Q[front]; for (int i=head[u];i;i=edge[i].next){ v=edge[i].to; if (dis[u]+edge[i].len<dis[v]){ dis[v]=dis[u]+edge[i].len; if (!vis[v]) push(v); } } front=(front+1)%maxn; } } int main(){ freopen("nuggets.in","r",stdin); freopen("nuggets.out","w",stdout); scanf("%d",&n); T=1<<30; for (int i=1;i<=n;i++) scanf("%d",&a[i]),T=min(T,a[i]); for (int i=1;i<=n;i++) for (int j=0;j<T;j++) makedge(j,(j+a[i])%T,a[i]); spfa(); ll ans=0; for (int i=0;i<T;i++) if (dis[i]>i) ans=max(ans,dis[i]-T); if (ans<oo) printf("%lld\\n",ans); else puts("0"); return 0; }
以上是关于USACO4.1.1--Beef McNuggets的主要内容,如果未能解决你的问题,请参考以下文章