DAG上的动态规划(硬币问题)
问题描述:
有n种硬币,面值分别为v1,v2,v3...vn,每种硬币有无限多,给定非负整数s,可以选用多少个硬币,使得面值之和恰好为s?输出硬币数目的最小值和最大值,并且输出各自的选取方案(如果有多种方案,则输出硬币编号字典序较小的方案,输出每种选取方案的面值)。1<=n<=100,0<=s<=10000,1<=Vi<=s.
分析:本质上是一个DAG上的路径问题,我们把每种面值看做一个点,表示还需凑足的面值,则初始状态为0,目标状态为0,若当前在i,则每使用一枚硬币j,状态转移到i-vj。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <algorithm> using namespace std; #define N 1100 int v[N],minm[N],maxm[N],min_coins[N],max_coins[N]; void print_ans(int *d,int s, int n) { while(s) { printf("%d ",v[d[s]]); s-=v[d[s]]; } printf("\n"); } int main() { //freopen("in.txt","r",stdin); int t,i,j,n,s; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&s); for(i=0; i<n; i++) scanf("%d",&v[i]); minm[0]=maxm[0]=0; for(i=1; i<=s;i++) { minm[i]=0x7FFFFFFF; maxm[i]=-0x7FFFFFFF; } for(i=1; i<=s; i++) { for(j=0; j<n; j++) { if(i>=v[j]) { if(minm[i]>minm[i-v[j]]+1) { minm[i]=minm[i-v[j]]+1; min_coins[i]=j; } if(maxm[i]<maxm[i-v[j]]+1) { maxm[i]=maxm[i-v[j]]+1; max_coins[i]=j; } } } } printf("%d %d\n",minm[s],maxm[s]); print_ans(min_coins,s,n); print_ans(max_coins,s,n); } return 0; }