题解 【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\n");
        return 0;
    }
    printf("%d %d\n",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("\n");
    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("\n");
}

猜你喜欢

转载自www.cnblogs.com/zsq259/p/10457896.html