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("
    ");
    }
  • 相关阅读:
    洛谷 P1119 灾后重建
    [NOIP 2011] 观光公交
    AtCoder Beginner Contest 071 D
    UOJ #228. 基础数据结构练习题
    bzoj 3673&3674: 可持久化并查集 by zky
    bzoj 2427: [HAOI2010]软件安装
    都市环游
    poj2299——Ultra-QuickSort
    poj1958——Strange Towers of Hanoi
    洛谷 P4139 上帝与集合的正确用法
  • 原文地址:https://www.cnblogs.com/zsq259/p/10457896.html
Copyright © 2011-2022 走看看