usaco training 5.3.1 Milk Measuring 题解

【原题】

  

Milk Measuring
Hal Burch

Farmer John must measure Q (1 <= Q <= 20,000) quarts of his finest milk and deliver it in one big bottle to a customer. He fills that bottle with exactly the number of quarts that the customer orders.

Farmer John has always been frugal. He is at the cow hardware store where he must purchase a set of pails with which to measure out Q quarts of milk from his giant milk tank. Since the pails each cost the same amount, your task is to figure out a minimal set of pails Farmer John can purchase in order to fill a bottle with exactly Q quarts of milk. Additionally, since Farmer John has to carry the pails home, given two minimal sets of pails he should choose the "smaller" one as follows: Sort the sets in ascending order. Compare the first pail in each set and choose the set with the smallest pail. If the first pails match, compare the second pails and choose from among those, else continue until the two sets differ. Thus the set {3, 5, 7, 100} should be chosen over {3, 6, 7, 8}.

To measure out milk, FJ may completely fill a pail from the tank and pour it into the bottle. He can never remove milk from the bottle or pour milk anywhere except into the bottle. With a one-quart pail, FJ would need only one pail to create any number of quarts in a bottle. Other pail combinations are not so convenient.

Determine the optimally small number of pails to purchase, given the guarantee that at least one solution is possible for all contest input data.

PROGRAM NAME: milk4

INPUT FORMAT

Line 1:The single integer Q
Line 2:A single integer P (1 <= P <= 100) which is the number of pails in the store
Lines 3..P+2:Each line contains a single integer pail_value (1 <= pail_value <= 10000), the number of quarts a pail holds

SAMPLE INPUT (file milk4.in)

16
3
3
5
7

OUTPUT FORMAT

The output is a single line of space separated integers that contains:

  • the minimum number of pails required to measure out the desired number of quarts, followed by:
  • a sorted list (from smallest to largest) of the capacity of each of the required pails

SAMPLE OUTPUT (file milk4.out)

2 3 5


【译题】

描述

农夫约翰要量取 Q(1 <= Q <= 20,000)夸脱(夸脱,quarts,容积单位——译者注) 他的最好的牛奶,并把它装入一个大瓶子中卖出。消费者要多少,他就给多少,从不有任何误差。

农夫约翰总是很节约。他现在在奶牛五金商店购买一些桶,用来从他的巨大的牛奶池中量出 Q 夸脱的牛奶。每个桶的价格一样。你的任务是计算出一个农夫约翰可以购买的最少的桶的集合,使得能够刚好用这些桶量出 Q 夸脱的牛奶。另外,由于农夫约翰必须把这些桶搬回家,对于给出的两个极小桶集合,他会选择“更小的”一个,即:把这两个集合按升序排序,比较第一个桶,选择第一个桶容积较小的一个。如果第一个桶相同,比较第二个桶,也按上面的方法选择。否则继续这样的工作,直到相比较的两个桶不一致为止。例如,集合 {3,5,7,100} 比集合 {3,6,7,8} 要好。

为了量出牛奶,农夫约翰可以从牛奶池把桶装满,然后倒进瓶子。他决不把瓶子里的牛奶倒出来或者把桶里的牛奶倒到别处。用一个容积为 1 夸脱的桶,农夫约翰可以只用这个桶量出所有可能的夸脱数。其它的桶的组合没有这么方便。

计算需要购买的最佳桶集,保证所有的测试数据都至少有一个解。

[编辑]格式

PROGRAM NAME: milk4

INPUT FORMAT

Line 1: 一个整数 Q

Line 2: 一个整数P(1 <= P <= 100),表示商店里桶的数量

Lines 3..P+2: 每行包括一个桶的容积(1 <= 桶的容积 <= 10000)

OUTPUT FORMAT

输出文件只有一行,由空格分开的整数组成:

为了量出想要的夸脱数,需要购买的最少的桶的数量,接着是:

一个排好序的列表(从小到大),表示需要购买的每个桶的容积

[编辑]SAMPLE INPUT (file milk4.in)

16
3
3
5
7

[编辑]SAMPLE OUTPUT (file milk4.out)

2 3 5


【想法一】刚开始觉得可以用无限DP做,f[i]表示到i体积时可以装的最小桶数。为了记录最佳的桶的组合,我还设了一个queue数组。queue[i][j]表示到i体积时的最佳选择是否选上编号为j的这个桶。同时要考虑很多细节,程序如下。

【漏洞】把错误的点带进去:组成59,有7、11、13等桶。显然7和13就行,7*1+13*4=59。但是当我推到i=3(即a[i]=13),j=33时更新不上去了。因为选7和13要两个,而33可以只选11,只需一个。虽然目前33是局部最优,但是它不会是全局最优!于是这样做DP会有问题!

【代码(WA了几个点)】

/*
PROG:wissqu
ID:juan1973
LANG:C++
*/
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#define INF 1010580540
using namespace std;
const int maxn=101;const int Q=20001;
bool flag;
int q,N,n,i,x,j,k,a[maxn],f[Q],small[Q],add;
bool queue[Q][maxn];
int main()
{
  //freopen("wissqu.in","r",stdin);
  //freopen("wissqu.out","w",stdout);
  scanf("%ld",&q);
  scanf("%ld",&N);
  for (i=1;i<=N;i++) 
  {
    scanf("%ld",&x);flag=false;
    for (j=1;j<=n;j++)
      if (a[j]%x==0) {a[j]=x;break;}
      else if (x%a[j]==0) {flag=true;break;}
    if (!flag) a[++n]=x;
  }
  sort(a+1,a+n+1);
  memset(f,60,sizeof(f));
  f[0]=0;
  for (i=1;i<=n;i++)
    for (j=a[i];j<=q;j++)
      if (f[j-a[i]]<INF)
      {
        if (queue[j-a[i]][i]) add=0;else add=1;
        if (f[j-a[i]]+add<f[j])
        {
          for (k=1;k<=n;k++)
            queue[j][k]=queue[j-a[i]][k];
          queue[j][i]=true;
          f[j]=f[j-a[i]]+add;
        }
        else if (f[j-a[i]]+add==f[j])
        {
          flag=true;
          for (k=1;k<=n;k++)
            if (queue[j-a[i]][k]&&!queue[j][k]) {flag=false;break;}
            else if ((!queue[j-a[i]][k]||i==k)&&queue[j][k]) break;
          if (!flag) 
            {for (k=1;k<=n;k++) queue[j][k]=queue[j-a[i]][k];queue[j][i]=true;}
        }
      }
  printf("%ld",f[q]);
  for (i=1;i<=n;i++)
    if (queue[q][i]) printf(" %ld",a[i]);
  printf("\n");
  return 0;
}

【想法二】那么只能深搜了。根据题目,本题可以用dfsid(类似于宽搜),随便加了点剪枝就过了。

/*
PROG:milk4
ID:juan1973
LANG:C++
*/
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#define INF 1010580540
using namespace std;
const int maxn=101;const int Q=20001;
int a[maxn],N,n,q,x,i,j,b[maxn],ans[maxn];
bool ok;
void dfsid(int num,int now,int use)
{
  if (use==i+1)
  {
    if (now==q) 
    {
      for (int j=1;j<=i;j++)
        ans[j]=b[j];
      ok=true;
    }
    return;
  }
  if (num>n||a[num]>ans[use]) return;
  int m=1;b[use]=a[num];
  while (now+a[num]*m<=q)
  {
    dfsid(num,now+a[num]*m,use+1);
    m++;
  }
  if (num<n) dfsid(num+1,now,use);
}
int main()
{
  freopen("milk4.in","r",stdin);
  freopen("milk4.out","w",stdout);
  scanf("%ld",&q);
  scanf("%ld",&N);
  for (i=1;i<=N;i++) 
  {
    scanf("%ld",&x);ok=false;
    for (j=1;j<=n;j++)
      if (a[j]%x==0) {a[j]=x;break;}
      else if (x%a[j]==0) {ok=true;break;}
    if (!ok) a[++n]=x;
  }
  sort(a+1,a+n+1);
  for (i=1;i<=n;i++)
  {
    memset(ans,60,sizeof(ans));
    dfsid(1,0,1);
    if (ok) break;
  }
  printf("%ld",i);
  for (j=1;j<=i;j++) printf(" %ld",ans[j]);
  printf("\n");
  return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值