题目:有n种硬币,面值分别为V1,V2,...Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值!
分析:我们把每种面值看作一个点!表示“还需要凑足的面值”,初始状态为S,目标状态为0。那么若当前状态在i,每使用一个硬币j,状态便转移到i-Vj。
#include <iostream>
#include <cstdio>
#define max(a,b) (a>b?a:b)
const int maxn = 10000 +10 ;
const int inf = 1<<30 ;
using namespace std;
int vis[maxn] , d[maxn] ; // vis标记是否访问 , d临时代替maxv ,minv数组
int maxv[maxn] , minv[maxn] ;// maxv存硬币最大数 , minv 存硬币最少数
int max_coin[maxn] , min_coin[maxn] ; // 存路径
int V[maxn] ; // 硬币价值
int n , S ;
///
int dpmaxx(int S) {
if( vis[S] ) return d[S] ;
vis[S] = 1 ;
int& ans = d[S] ;
ans = -(1<<30) ;
for ( int i = 1 ; i <= n ; ++i ) {
if( S >= V[i] ) {
ans = max ( ans , dpmaxx(S-V[i]) + 1 ) ;
}
}
return ans ;
}
int dpminn( int S) {
if(vis[S] ) return d[S] ;
vis[S] = 1 ;
int& ans = d[S] ;
ans = 1<<30 ;
for( int i = 1 ; i <= n ; ++i ){
if( S >= V[i] ) {
ans = min( ans , dpminn(S- V[i]) + 1) ;
}
}
}
int dpmax( int S) {
int& ans = d[S] ;
if( ans != -1 ) return ans ;
ans = -(1<<30) ;
for ( int i = 1 ; i <= n ; ++i ) {
if( S >= V[i] ) ans = max( ans , dpmax(S-V[i]) + 1) ;
}
return ans ;
}
int dpmin( int S) {
int& ans = d[S] ;
if( ans != -1 )
return ans ;
ans = 1<<30 ;
for( int i = 1 ; i <= n ; ++i ) {
if( S >= V[i] ) ans = min( ans , dpmin(S-V[i]) + 1) ;
}
return ans ;
}
///
/
//输出字典序最小的方案 方法1
void print_ans( int* d , int S) {
for( int i = 1 ; i <= n ; ++i ) {
if( S >= V[i] && d[S] == d[S-V[i]] +1 ) {
printf("%d ",i) ;
print_ans(d , S-V[i]) ;
break ;
}
}
}
void print_ans2( int* d , int S) {
while(S) {
printf("%d ",d[S]) ;
S -= V[d[S]] ;
}
}
int main() {
while(~scanf("%d%d",&n,&S) ) {
minv[0] = maxv[0] = 0 ;
for( int i = 1 ; i <= S ; ++i ) {
minv[i] = inf ;
maxv[i] = -inf ;
}
for( int i = 1 ; i <= n ; ++i ) {
scanf("%d",&V[i]) ;
}
for( int i = 1 ; i <= S ; ++i ) {
for( int j = 1 ; j <= n ; ++j ) {
if( i >= V[j]) {
minv[i] = min( minv[i] , minv[i-V[j]] + 1) ;
maxv[i] = max( maxv[i] , maxv[i - V[j] ] + 1) ;
///
// 迭代打印路径 方法2:
if( minv[i] > minv[i - V[j]] + 1 ) {
minv[i] = minv[i - V[j]] + 1 ;
min_coin[i] = j ;
}
if( maxv[i] < maxv[i - V[j]] + 1 ) {
maxv[i] = maxv[i - V[j]] + 1 ;
max_coin[i] = j ;
}
//
}
}
}
printf("%d %d\n", minv[S] , maxv[S] ) ;
print_ans(maxv,S) ;
print_ans(minv,S) ;
///
print_ans2(maxv,S) ;
print_ans2(minv,S) ;
///
}
return 0 ;
}