DAG上的动态规划------硬币问题

                                              
题目:有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 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值