DAG模型--硬币(动态规划,记忆化搜索)

DAG模型–硬币
有n种硬币,面值分别为V1,V2,…,Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值。1<=n<=100,0<=S<=10000,1<=Vi<=S。

思路:
该题本质还是DAG得路径问题,每种面值都看作一个点,设出状态maxv(x)和minv(x),分别表示从当前结点到0结点的最长和最短路径,找出状态转移方程,初始状态为S,选出最优解。
需要注意的是,记忆化搜索可以减少大量的时间,但是此题的结果里面路径可以为0,所以全部都标记-1以区分,大于等于0时说明计算过,直接返回值,否则标记为0,然后进行搜索计算。
但是这样会产生错误,此题可能存在无解,也就是最后找不到重点,凑不齐S,那么这时便会错认为找到了。所以这里应该对算过的用其他特殊值区分开,即极小的值和极大的值,状态初始全部标记-1,只有终点用0标记。

代码如下:

#include <iostream>
#include <algorithm>
#include <string.h>
#define maxn 105
#define maxvn 10005
using namespace std;
int n,s;
int v[maxn],maxv[maxvn],minv[maxvn];
int dpmax(int s)
{
    
    
 int i;
 int& ans=maxv[s];
 if(ans!=-1)
 return ans;
 ans=-(1<<30);
 for(i=1;i<=n;i++)
 {
    
    
  if(s>=v[i])
  {
    
    
   ans=max(ans,dpmax(s-v[i])+1);
  }
 }
 return ans;
}
int dpmin(int s)
{
    
    
 int i;
 int& ans=minv[s];
 if(ans!=-1)
 return ans;
 ans=(1<<30);
 for(i=1;i<=n;i++)
 {
    
    
  if(s>=v[i])
  {
    
    
   ans=min(ans,dpmin(s-v[i])+1);
  }
 }
 return ans;
}
int print_max(int s)
{
    
    
 int i;
 for(i=1;i<=n;i++)
 {
    
    
  if(s>=v[i]&&maxv[s]==maxv[s-v[i]]+1)
  {
    
    
   cout<<v[i]<<" ";
   print_max(s-v[i]);
   break;
  }
 }
}
int print_min(int s)
{
    
    
 int i;
 for(i=1;i<=n;i++)
 {
    
    
  if(s>=v[i]&&minv[s]==minv[s-v[i]]+1)
  {
    
    
   cout<<v[i]<<" ";
   print_min(s-v[i]);
   break;
  }
 }
}
int main()
{
    
    
 int i;
 cin>>n>>s;
 for(i=1;i<=n;i++)
 {
    
    
  cin>>v[i];
 }
 memset(maxv,-1,sizeof(maxv));
 maxv[0]=0;
 memset(minv,-1,sizeof(minv));
 minv[0]=0;
 cout<<"min="<<dpmin(s)<<endl;
 cout<<"The Path Of Min:"<<endl;
 print_min(s);
 cout<<endl;
 cout<<"max="<<dpmax(s)<<endl;
 cout<<"The Path Of Max:"<<endl;
 print_max(s);
 cout<<endl;
 return 0;
} 

猜你喜欢

转载自blog.csdn.net/HT24k/article/details/106251537