题解【CJOJ1071/UVA】硬币问题

Description

有n种硬币,面值分别为v1, v2, ..., vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。

Input

第一行两个整数,n,S(1≤n≤100, 0≤S≤100000)。
第二行n个整数vi-1...n(1≤vi≤S)。

Output

第一行两个整数,分别表示硬币数目的最小值 a 和最大值 b 。无解则输出 -1 。
第二行 a 个整数分别表示使用的是第几种硬币。
第三行 b 个整数分别表示使用的是第几种硬币。

Sample Input

6 12 
1 2 3 4 5 6

Sample Output

2 12 
6 6 
1 1 1 1 1 1 1 1 1 1 1 1

Hint

样例是特殊的,编号和面值是相同的。你编写程序的时候要注意输出编号而不是面值。
结果按编号升序输出字典序小一种。

Source

入门经典,DP,DAG

Solution

考虑DP。

对于每个值i(1<=i<=s),我们可以枚举j,使得i从i-v[j]增加一个v[j]得来。因此,我们有了下面的代码片段:

for(register int i=1; i<=s; i++)
    {
        for(register int j=1; j<=n; j++)
        {
            if(i-v[j]<0)
            {
                continue;
            }
            else
            {
                if(mi[i]>mi[i-v[j]]+1)
                {
                    mi[i]=mi[i-v[j]]+1;

                    pi[i]=i-v[j];
                }

                if(mx[i]<mx[i-v[j]]+1)
                {
                    mx[i]=mx[i-v[j]]+1;

                    px[i]=i-v[j];
                }
            }
        }
    }

这样一来,题目就迎刃而解了!

Code

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 inline int read()
  6 {
  7     int f=1,x=0;
  8     char c=getchar();
  9 
 10     while(c<'0' || c>'9')
 11     {
 12         if(c=='-')f=-1;
 13         c=getchar();
 14     }
 15 
 16     while(c>='0' && c<='9')
 17     {
 18         x=x*10+c-'0';
 19         c=getchar();
 20     }
 21 
 22     return f*x;
 23 }
 24 
 25 int s1,n,v[105],s,a,b,ma,mb,d[100005],mi[100005],mx[100005],pi[100005],px[100005];
 26 int an[100005];
 27 
 28 int main()
 29 {
 30     n=read(),s=read();
 31 
 32     for(register int i=1; i<=n; i++)
 33     {
 34         v[i]=read();
 35         
 36         d[v[i]]=i;
 37     }
 38 
 39     for(register int i=1; i<=s; i++)
 40     {
 41         mi[i]=1000000007;//组成值i最少需要多少枚硬币
 42         mx[i]=-1000000007;//组成值i最多需要多少枚硬币
 43     }
 44 
 45     mi[0]=0;
 46     mx[0]=0;
 47 
 48     for(register int i=1; i<=s; i++)
 49     {
 50         for(register int j=1; j<=n; j++)
 51         {
 52             if(i-v[j]<0)
 53             {
 54                 continue;
 55             }
 56             else
 57             {
 58                 if(mi[i]>mi[i-v[j]]+1)
 59                 {
 60                     mi[i]=mi[i-v[j]]+1;
 61 
 62                     pi[i]=i-v[j];
 63                 }
 64 
 65                 if(mx[i]<mx[i-v[j]]+1)
 66                 {
 67                     mx[i]=mx[i-v[j]]+1;
 68 
 69                     px[i]=i-v[j];
 70                 }
 71             }
 72         }
 73     }
 74 
 75     if(mi[s]==1000000007 || mx[s]==-1000000007)//如果组成不了s
 76     {
 77         printf("-1");//输出无解
 78 
 79         return 0;
 80     }
 81 
 82     printf("%d %d\n",mi[s],mx[s]);//输出最小组成数
 83    //输出方案
 84     s1=s;
 85     
 86     int T=0;
 87     
 88     memset(an,0,sizeof(an));
 89 
 90     while(s1>0)
 91     {
 92         an[++T]=d[s1-pi[s1]];
 93 
 94         s1=pi[s1];
 95     }
 96 
 97     sort(an+1,an+1+T);
 98 
 99     for(register int i=1; i<=T; i++)
100     {
101         printf("%d ",an[i]);
102     }
103 
104     puts("");
105 
106     s1=s;
107 
108     T=0;
109     
110     memset(an,0,sizeof(an));
111 
112     while(s1>0)
113     {
114         an[++T]=d[s1-px[s1]];
115 
116         s1=px[s1];
117     }
118 
119     sort(an+1,an+1+T);
120 
121     for(register int i=1; i<=T; i++)
122     {
123         printf("%d ",an[i]);
124     }
125 
126     return 0;//结束
127 }

 

 

 

posted @ 2019-03-01 17:29  csxsi  阅读(356)  评论(0编辑  收藏  举报