zoukankan      html  css  js  c++  java
  • 题解 【Uva】硬币问题

    【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

    解析

    这题一看就要用DP啊啊!

    然而却不知道怎么用qwq!!!

    其实可以把它看成是DAG,

    每一个点(即不同面值)可以通过许多条权值为1的边到达其它点(即增加一枚硬币)。

    因此,只要求最短路和最长路就行了。

    另外,硬币的方案可以用一个father数组来记录,只要在更新路径的时候一起更新就行了。

    上AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    int n,s,v[101],num[100001];
    int dmin[100001]/*最小路径长度*/,famin[100001]/*上一个硬币*/,ansmin[100001]/*打印路径*/,mincnt=0/*硬币个数*/;
    int dmax[100001],famax[100001],ansmax[100001],maxcnt=0;/*max和min一样(下同)*/
    int que[10000001],vis[100001];
    
    int main(){
        scanf("%d%d",&n,&s);
        for(int i=1;i<=n;i++){
            scanf("%d",&v[i]);
            num[v[i]]=i;
        }
        vis[0]=1;
        memset(dmin,0x3f,sizeof(dmin));
        memset(dmax,-0x3f,sizeof(dmax));
        dmin[0]=0;dmax[0]=0;
        for(int i=0;i<=s;i++)/*枚举面值*/{
            for(int j=1;j<=n;j++)/*枚举到达当前面值的方式*/{
                int k=i-v[j];
                if(k<0) break;
                if(dmin[i]>dmin[k]+1)/*更新*/{
                    dmin[i]=dmin[k]+1;
                    famin[i]=k;
                }
                if(dmax[i]<dmax[k]+1){
                    dmax[i]=dmax[k]+1;
                    famax[i]=k;
                }
                
            }
        }
        if(dmin[s]==0x3f3f3f3f||dmax[s]==0){
            printf("-1
    ");
            return 0;
        }
        printf("%d %d
    ",dmin[s],dmax[s]);
        for(int i=s;i;i=famin[i])/*打印路径*/{
            int k=famin[i];
            ansmin[++mincnt]=num[i-k];
        }
        sort(ansmin+1,ansmin+mincnt+1);//这一步似乎不需要(重构时懒得删了)
        for(int i=1;i<=mincnt;i++) printf("%d ",ansmin[i]);
        printf("
    ");
        for(int i=s;i;i=famax[i]){
            int k=famax[i];
            ansmax[++maxcnt]=num[i-k];
        }
        sort(ansmax+1,ansmax+maxcnt+1);
        for(int i=1;i<=maxcnt;i++) printf("%d ",ansmax[i]);
        printf("
    ");
    }
  • 相关阅读:
    @+id/和android:id的区别
    android.intent.action.MAIN 与 android.intent.category.LAUNCHER 的验证理解
    jQuery对象与dom对象相互转换
    javascript中apply和eval结合的强大用法
    公司项目学习杂烩
    c#(winform,webform通用)利用npoi将xls文件复制为xlsx文件(excel的修改,保存,包括excel2003-office2007+的处理)
    uploadfiy使用
    我都学了些什么
    给编程一个你热爱它的机会
    野心不能成就你,热爱却可以
  • 原文地址:https://www.cnblogs.com/zsq259/p/10457896.html
Copyright © 2011-2022 走看看