01背包记录路径 (例题 L3-001 凑零钱 (30分))

 

 

 

题意:

就是找出来一个字典序最小的硬币集合,且这个硬币集合里面所有硬币的值的和等于题目中的M

 

题解:

01背包加一下记录路径,如果1硬币不止一个,那我们也不采用多重背包的方式,把每一个1硬币当成一个独立的单位来进行01背包dp

 

但是我们知道背包dp的路径可能不止一条,而我们要从中得到字典序最小的序列,我的代码中两次不同的排序会得到最大/小字典序

降序 == 最小字典序

升序 == 最大字典序

 

 1 #include<iostream>
 2 #include<queue>
 3 #include<vector>
 4 #include<string.h>
 5 #include<stdio.h>
 6 #include<algorithm>
 7 using namespace std;
 8 typedef long long ll;
 9 const int maxn=5e4+10;
10 const int N=1e4+10;
11 int v[N];
12 int vv[N];
13 int dp[N];
14 bool pre[N][105];
15 int main()
16 {
17     int m,n;
18     scanf("%d%d",&n,&m);
19     for(int i=1; i<=n; i++)
20     {
21         scanf("%d",&v[i]);
22     }
23     sort(v+1,v+n+1);  //从大到小排序那么答案输出就是最小字典序,如果是从小到大排序,那么答案输出
24     //就是最大字典序(这就是结论),greater<int>()
25     memset(dp,0,sizeof(dp));
26     memset(pre,0,sizeof(pre));
27     for(int i=1; i<=n; i++)
28     {
29         for(int j=m; j>=v[i]; j--)
30         {
31             if(dp[j]<=dp[j-v[i]]+v[i])
32             {
33                 dp[j]=dp[j-v[i]]+v[i];
34                 pre[i][j]=1;//如果第i个歌曲被放进背包,,标记当前背包的位置,记录路径;
35             }
36         }
37     }
38     if(dp[m]!=m)
39     {
40         printf("No Solution\n");
41         return 0;
42     }
43     int i=n,j=m,flag=0;  //这里的初赋值可不能随意改(控制输出最后最大/小字典序就是sort排序和这里,至于为什么可以画一下图)
44     while(i>=1&&j>=0)
45     {
46         if(pre[i][j])
47         {
48             if(!flag)
49                 printf("%d",v[i]),flag=1;
50             else printf(" %d",v[i]);
51             j=j-v[i];//第i张歌曲放在了第当背包容量为j-v[i]时,下一步找第i-1张歌曲.(由此的来dp[j]=dp[j-v[i]]+v[i];)
52         }
53         i--;
54     }
55     return 0;
56 }

 

 

我对多重背包记录路径和完全背包记录路径

1、对于多重背包记录路径

第一种方法就是像这一道题一样,把多重背包当作01背包来写,但是这样时间复杂度肯定高

2、完全背包都不限制数量了,那记录路径就没有意义了

 

posted @ 2020-03-27 14:34  kongbursi  阅读(753)  评论(0编辑  收藏  举报