码迷,mamicode.com
首页 > 其他好文 > 详细

USACO4.1.1--Beef McNuggets

时间:2017-08-25 00:06:23      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:freopen   break   ini   问题   sizeof   typedef   最大   for   push   

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

标签:freopen   break   ini   问题   sizeof   typedef   最大   for   push   

原文地址:http://www.cnblogs.com/chunlvxiong/p/7425549.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!