zoukankan      html  css  js  c++  java
  • 题解【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
    ",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 }

     

  • 相关阅读:
    [转载]Back up all of your mysql databases nightly
    编写windows7 bat运行脚本
    Windows7开通Internet信息服务
    【LeetCode题解】7_反转整数
    【LeetCode题解】350_两个数组的交集Ⅱ
    【LeetCode题解】349_两个数组的交集
    【LeetCode题解】94_二叉树的中序遍历
    【LeetCode题解】144_二叉树的前序遍历
    【LeetCode题解】2_两数相加
    【LeetCode题解】530_二分搜索树的最小绝对值差
  • 原文地址:https://www.cnblogs.com/xsl19/p/10457732.html
Copyright © 2011-2022 走看看