USACO section 4.1 Beef McNuggets(数论+背包)

Beef McNuggets
Hubert Chen

Farmer Brown's cows are up in arms, having heard that McDonalds is considering the introduction of a new product: Beef McNuggets. The cows are trying to find any possible way to put such a product in a negative light.

One strategy the cows are pursuing is that of `inferior packaging'. ``Look,'' say the cows, ``if you have Beef McNuggets in boxes of 3, 6, and 10, you can not satisfy a customer who wants 1, 2, 4, 5, 7, 8, 11, 14, or 17 McNuggets. Bad packaging: bad product.''

Help the cows. Given N (the number of packaging options, 1 <= N <= 10), and a set of N positive integers (1 <= i <= 256) that represent the number of nuggets in the various packages, output the largest number of nuggets that can not be purchased by buying nuggets in the given sizes. Print 0 if all possible purchases can be made or if there is no bound to the largest number.

The largest impossible number (if it exists) will be no larger than 2,000,000,000.

PROGRAM NAME: nuggets

INPUT FORMAT

Line 1: N, the number of packaging options
Line 2..N+1: The number of nuggets in one kind of box

SAMPLE INPUT (file nuggets.in)

3
3
6
10

OUTPUT FORMAT

The output file should contain a single line containing a single integer that represents the largest number of nuggets that can not be represented or 0 if all possible purchases can be made or if there is no bound to the largest number.

SAMPLE OUTPUT (file nuggets.out)

17

思路:有个结论,有两个数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。直接进行背包就可以了。


/*
ID:nealgav1
LANG:C++
PROG:nuggets
*/
#include<fstream>
#include<cstdio>
#include<cstring>
using namespace std;
ifstream cin("nuggets.in");
ofstream cout("nuggets.out");
const int mm=266;
int f[mm];
int dp[mm*mm+3];
int gcd(int a,int b)
{ int z;
  while(a)
  {
    z=a;a=b%a;b=z;
  }
  return b;
}
void Min(int&x,const int y)
{
  if(x>y)x=y;
}
int main()
{ int n;
  while(cin>>n)
  { bool flag=0;
    for(int i=0;i<n;++i)
      {cin>>f[i];if(f[i]==1)flag=1;}
    int z=gcd(f[0],f[1]);
    for(int i=1;i<n;++i)
      z=gcd(z,f[i]);
    if(z>1)flag=1;
    int pos=mm*mm;
    for(int i=0;i<n;++i)
      for(int j=0;j<n;++j)
      if(i^j&&gcd(f[i],f[j])==1)
      Min(pos,f[i]*f[j]-f[i]-f[j]);
    memset(dp,0,sizeof(dp));
    int ans=0;dp[0]=1;
    z=mm*mm;
    for(int i=0;i<z;++i)
      if(dp[i])
      { for(int j=0;j<n;++j)
        if(i+f[j]<z)
        dp[i+f[j]]=1;
      }else if(ans<i)ans=i;
      if(ans>pos||ans>256*256)ans=0;
  ///  for(int i=0;i<=pos;++i)
   ///   cout<<i<<" "<<dp[i]<<endl;
    ///if(flag)ans=0;
    cout<<ans<<"\n";
  }
}



USER: Neal Gavin Gavin [nealgav1]
TASK: nuggets
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 3632 KB]
   Test 2: TEST OK [0.000 secs, 3632 KB]
   Test 3: TEST OK [0.000 secs, 3632 KB]
   Test 4: TEST OK [0.000 secs, 3632 KB]
   Test 5: TEST OK [0.000 secs, 3632 KB]
   Test 6: TEST OK [0.000 secs, 3632 KB]
   Test 7: TEST OK [0.011 secs, 3632 KB]

All tests OK.

Your program ('nuggets') produced all correct answers! This is your submission #3 for this problem. Congratulations!

Here are the test data inputs:

------- test 1 ----
3
3
6
10
------- test 2 ----
2
2
3
------- test 3 ----
1
1
------- test 4 ----
4
252
250
254
256
------- test 5 ----
2
255
254
------- test 6 ----
5
251
252
250
254
256
------- test 7 ----
10
238
240
242
244
246
248
250
252
254
255
Beef McNuggets
Hal Burch

This problem is fairly straight-forward dynamic programming. We know that a value X is possible if and only if X - vi is possible, where vi is the number of nuggets in one of the package types.

The only way for there to be no bound to the largest number which is unobtainable is if the greatest common divisor of the package sizes is greater than 1, so first check for that.

Otherwise, go through the sizes in increasing order. For each impossible value, update the largest number found thus far. Otherwise, if X is possible, mark X + vi for each i as being possible. Whenever the last 256 of the sizes have all been possible, you know that all the sizes from here on out are also possible (you actually only need the last min {vi} to be possible, but doing the extra 256 steps takes almost no time).

#include <stdio.h>

/* the number and value of the package sizes */
int nsize;
int sizes[10];

/* cando specifies whether a given number is possible or not */
/* since max size = 256, we'll never need to mark more than 256
   in the future, so we use a sliding window */
int cando[256];

int gcd(int a, int b)
 { /* uses standard gcd algorithm to computer greatest common divisor */
  int t;

  while (b != 0)
   {
    t = a % b;
    a = b;
    b = t;
   }
  return a;
 }

int main(int argc, char **argv)
 {
  FILE *fout, *fin;
  int lv, lv2; /* loop variable */
  int pos;     /* count position */
  int last;    /* last impossible count */

  if ((fin = fopen("nuggets.in", "r")) == NULL)
   {
    perror ("fopen fin");
    exit(1);
   }
  if ((fout = fopen("nuggets.out", "w")) == NULL)
   {
    perror ("fopen fout");
    exit(1);
   }

  /* read in data */
  fscanf (fin, "%d", &nsize);
  for (lv = 0; lv < nsize; lv++) fscanf (fin, "%d", &sizes[lv]);

  /* ensure gcd = 1 */
  lv2 = sizes[0];
  for (lv = 1; lv < nsize; lv++)
    lv2 = gcd(sizes[lv], lv2);
  if (lv2 != 1)
   { /* gcd != 1, no bound on size that cannot be purchased */
    fprintf (fout, "0\n");
    return 0;
   }

  /* we can do 0 */
  cando[0] = 1;

  lv = pos = 0;
  last = 0;
  while (pos < 2000000000)
   { /* bound as stated */

    /* if last 256 were all possible, we are done */
    if (pos - last > 256) break; 

    if (!cando[lv]) 
      last = pos; /* this isn't possible, update last impossible */
    else 
     { /* this is possible */
      cando[lv] = 0; /* mark pos+256 as impossible */

      /* mark pos + size as possible for each package size */
      for (lv2 = 0; lv2 < nsize; lv2++)
        cando[(lv+sizes[lv2])%256] = 1;
     }

    /* update lv & pos */
    lv = (++pos) % 256; 
   }
  if (pos >= 2000000000) last = 0; /* shouldn't occur */

  fprintf (fout, "%i\n", last);
  return 0;
 }

Alex Schwendner's Comments

Given two relatively prime numbers N and M, the largest number that you cannot make is NM - M - N, that is, the product minus the sum. We do not have two numbers; however, even if we were using only two of them the answer could not exceed 256 * 256 - 256 - 256 = 65024 (much less then the 2,000,000,000 that we were guaranteed). It is therefore reasonable to have an array of 65024 booleans and work on all of them (If cando[x] then cando[x + sizes[p]]). If there is some number above 65024 that cannot be made then we know that there is no bound to the largest number. Because cando[0] is set to false, if every number can be made then the program will output '0' automatically. This program is shorter and easier to code, and although it is somewhat less efficient, it is easily able to solve the problem in the time limit.

#include <fstream.h>
#include <string.h>

int 
main ()
{

    int     n;
    int     sizes[10];

    ifstream filein ("nuggets.in");
    filein >> n;
    for (int in = 0; in < n; ++in) {
	filein >> sizes[in];
    }
    filein.close ();

    bool    cando[67000];
    memset (cando, 0, 67000);

    for (int loop = 0; loop < n; ++loop) {
	cando[sizes[loop]] = true;
	for (int loop2 = 0; loop2 < 66700; ++loop2) {
	    if (cando[loop2]) {
		cando[loop2 + sizes[loop]] = true;
	    }
	}
    }

    ofstream fileout ("nuggets.out");
    for (int out = 66500; out >= 0; --out) {
	if (!cando[out]) {
	    if (out < 66000) {
		fileout << out << endl;
		break;
	    }
	    else {
		fileout << 0 << endl;
		break;
	    }
	}
    }
    fileout.close ();

    return (0);
}

posted @ 2013-03-26 10:45  剑不飞  阅读(240)  评论(0编辑  收藏  举报