#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1e5;int dp[mxn];intmain(){/* fre(); */int n;while(~sd(n)){memset(dp,0,sizeof dp);
dp[0]=1;for_(i,1,3)for_(j,0, n - i)
dp[j + i]+= dp[j];pr("%d\n", dp[n]);}return0;}
B - Investment POJ - 2063
思路
首先明确,在某个一年我们手中的总钱数为 m 的时候,要通过合理的选择来买股票,来获取最大利润,这是一个 完全背包问题,股票的价格是空间,股票的利润是:物品的价值;
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1e5;int dp[mxn];int val[mxn], num[mxn];int use[mxn];//use[i] 表示的是背包空间为i的时候,使用的当前的种类的硬币的数量int path[mxn];
map<int,int> ans;int n;intmain(){/* fre(); */
val[1]=1; val[2]=5; val[3]=10; val[4]=25;while(sd(n)&& n){
ans.clear();for_(i,1,4)sd(num[i]);for_(i,1, n) dp[i]=- INF;for_(i,1,4){memset(use,0,sizeof use);for_(j, val[i], n){//注意:在这一题中我们 dp值维护的是什么?,dp[i] 表示在背包空间为i的情况下 填满背包的最大硬币数量//注意:这里 第二层for循环是正向的,正向for循环在背包问题中是用来解决 “完全背包的”, 但是在这一题中 每种硬币的数量是有限的,是一个 “多重背包问题”, 但是我们同 use[] 数组的使用 来统计已经使用的当前种类的硬币的数量,当数量超过所给的该种的硬币的数量的时候,那么此时的状态转移是非法的,那么禁止这个时候的状态转移就行了if(use[j - val[i]]< num[i]&& dp[j - val[i]]>=0&& dp[j - val[i]]+1> dp[j]){
dp[j]= dp[j - val[i]]+1;
use[j]= use[j - val[i]]+1;//当前正在使用过的 第i种硬币数量 +1
path[j]= j - val[i];//很巧妙的记录路径, 在当前背包空间为j是,记录的当前方案的路径是从 背包空间j - val的状态注意过来,这样我们通过 j - (j - val) 就可以得出当前这一步的状态转移,是使用的第i种硬币}}}if(dp[n]<0){pr("Charlie cannot buy coffee.\n");continue;}int p = n;while(p){
ans[p - path[p]]++;
p = path[p];}pr("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[1], ans[5], ans[10], ans[25]);}return0;}
D - 饭卡 HDU - 2546
思路
这一题不是一个背包问题,感觉就像一道递推题,,,
我们先个 总钱数 m + 50, 这样我们在递推的时候不会出现下表为负的下标,
我们在刚开始标记 dp [m + 50] = 1, 其他的为零,之后
代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =2005;int dp[mxn];int v[mxn];intmain(){/* fre(); */int n;while(sd(n)&& n){for_(i,1, n)sd(v[i]);int m;sd(m);sort(v +1, v +1+ n);memset(dp,0,sizeof dp);
dp[m +50]=1;int ans = m +50;//注意ans必须初始化为 m + 50, 不能初始化为其他的如 INF 等,因为当 n == 0的时候,此时卡里面有多少钱,答案就是多少钱for_(i,1, n){for_(j,55, m +50){if(dp[j]){
dp[j - v[i]]=1;
ans =min(ans, j - v[i]);}}}pr("%d\n", ans -50);}return0;}
D - 饭卡 HDU - 2546 (贪心 +dp)
思路
这一题的动态规划不向之前的背包问题,这一球的是把卡里面的余额 m,通过买 n 中食物之后,能够把卡里面的余额最小变成多少,可以变成负数(当卡里面的余 >=5 时,即使卖了某个物品之后变成负数,这种情况也是允许的)
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =2005;int dp[mxn];int v[mxn];intmain(){/* fre(); */int n;while(sd(n)&& n){for_(i,1, n)sd(v[i]);int m;sd(m);sort(v +1, v +1+ n);memset(dp,0,sizeof dp);
dp[m +50]=1;int ans = m +50;//注意ans必须初始化为 m + 50, 不能初始化为其他的如 INF 等,因为当 n == 0的时候,此时卡里面有多少钱,答案就是多少钱for_(i,1, n){for_(j,55, m +50){if(dp[j]){
dp[j - v[i]]=1;
ans =min(ans, j - v[i]);}}}pr("%d\n", ans -50);}return0;}
E - Bone Collector II HDU - 2639
思路
经点的第 k 大问题,我们在 dp 的时候,我们对每个状态都维护前 k 大,之后下一个状态的前 k 大一定可以从当前状态的前 k 大转移过来,
代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1010;int f[mxn][mxn];int n, m, k;int v[mxn], w[mxn];int a[mxn], b[mxn];intmain(){/* fre(); */int T;sd(T);while(T --){sc("%d %d %d",&n,&m,&k);for_(i,1, n)sd(w[i]);for_(i,1, n)sd(v[i]);memset(f,0,sizeof f);for_(i,1, n){rep_(j, m, v[i]){for_(l,1, k)//处理两种情况的状态转移方程分别存储到a、b数组中{
a[l]= f[j][l];
b[l]= f[j - v[i]][l]+ w[i];}
a[k +1]= b[k +1]=-1;//结束标志int x =1, y =1, z =1;while((x <= k || y <= k)&& z <= k)//注意让求的第k大是不是严格第k大{if(a[x]> b[y]){
f[j][z]= a[x ++];}else{
f[j][z]= b[y ++];}if(f[j][z]!= f[j][z -1]) z ++;//非严格第k大 只有不想等 z 才++}}}pr("%d\n", f[m][k]);}return0;}
F - Coin Change UVA - 674
思路
参考 题解第 1 题
代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1e4;int dp[mxn];int cnt[mxn];int v[]={0,1,5,10,25,50};intmain(){/* fre(); */int n;while(sd(n)!=EOF){for_(i,1, n) dp[i]=- INF;memset(cnt,0,sizeof cnt);
cnt[0]=1;for_(i,1,5){for_(j, v[i], n){int val = dp[j - v[i]]+ v[i];if(val > dp[j]){
dp[j]=val;
cnt[j]= cnt[j - v[i]];}elseif(val == dp[j])
cnt[j]+= cnt[j - v[i]];}}pr("%d\n", cnt[n]);}return0;}
G - Dollars UVA - 147
思路
这一题我们注意浮点数扩大倍数产生精度问题,解决方案:假如有个浮点数 f,扩大 k 倍之后变成整数 n:
n
=
k
∗
f
+
0.5
n = k*f+0.5
n=k∗f+0.5 精度问题分析
代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1e5;
ll dp[mxn];
ll cnt[mxn];int v[]={0,1,2,4,10,20,40,100,200,400,1000,2000};intmain(){/* fre(); */
db t;while(sc("%lf",&t)&& t){int n = t *20;for_(i,1, n) dp[i]=-INF;memset(cnt,0,sizeof cnt);
cnt[0]=1;for_(i,1,11){for_(j, v[i], n){int val = dp[j - v[i]]+ v[i];if(val > dp[j]){
dp[j]= val;
cnt[j]= cnt[j - v[i]];}elseif(val == dp[j])
cnt[j]+= cnt[j - v[i]];}}pr("%6.2f%17lld\n", t, cnt[n]);}return0;}
H - 最大报销额 HDU - 1864
思路
01 背包问题,只不过我们需要对数据进行预处理,
有一点题意需要理解:每饭票上的 “某种类型的 x 金额不超过 600”,一张发票上可能会重复出现 x,而这些 x 类型的金额之和也不应该超过 600,,,,
这一有两种 dp 思路,一种就是将总金额 m = m* 100/5 ,之后将 m 作为背包的空间,进行 01 背包那样的 dp 就行了
我们可以看到 上面这段代码的第二层 for 循环 循环的是物品数量 n(而正常的 01 背包循环的是 背包的空间 m),那么它这样 for 循环合理吗?有意义吗?,
经过了一番模拟发现是合理的,在这段代码里:dp [j] 表示在当前有 i 个物品的时候,从 i 个物品中选择 j (j<=i) 个物品,可以产生的最大价值为:dp [j],
真是一种神器的 dp 方式。。。。
代码一
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<algorithm>#include<cstdio>
using namespace std;#define mem(a, i) memset(a, i, sizeof(a))#define INF 0x3f3f3f3fint dp[4999000];intmain(){//freopen("in.txt", "r", stdin);int n;double q;while(cin >> q >> n && n){int w[1010];int m;char s, c;mem(w,0);for(int i =1; i <= n;++ i){
cin >> m;
bool flag = true;double h, A =0, B =0, C =0, sum =0;for(int j =0; j < m;++ j){
c =getchar();
s =getchar();
c =getchar();if(!(s =='A'|| s =='B'|| s =='C')){
flag = false;}
cin >> h;if(s =='A'){
A += h;}elseif(s =='B'){
B += h;}elseif(s =='C'){
C += h;}}if(A >600|| B >600|| C >600){
flag = false;}else{
sum +=(A + B + C);}if(flag && sum <=1000){
w[i]=(sum *100);}else{
w[i]= INF;}}mem(dp,0);int p =(q *100);for(int i =1; i <= n;++ i){for(int j = q *100; j >=0;--j){if(j < w[i]){
dp[j]= dp[j];}else{
dp[j]=max(dp[j - w[i]]+ w[i], dp[j]);}}}printf("%.2lf\n", dp[p]*1.0/100);}return0;}
#include<iostream>#include<algorithm>#include<cstring>
using namespace std;constint mxn =1e5;int dp[mxn];int v[mxn], w[mxn];intmain(){int T;scanf("%d",&T);while(T--){int n, m;memset(dp,0,sizeof dp);scanf("%d %d",&n,&m);for(int i =0; i < n; i ++)scanf("%d",&w[i]);for(int i =0; i < n; i ++)scanf("%d",&v[i]);for(int i =0; i < n; i ++)for(int j = m; j >= v[i]; j --)
dp[j]=max(dp[j], dp[j - v[i]]+w[i]);printf("%d\n", dp[m]);}return0;}
J - Proud Merchants HDU - 3466
思路
代码
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<queue>#include<set>#include<bitset>#include<vector>#include<stack>#include<map>/* #include <unordered_map> */#include<sstream>voidfre(){system("clear"),freopen("A.txt","r",stdin);freopen("Ans.txt","w",stdout);}voidFre(){system("clear"),freopen("A.txt","r",stdin);}#define ios ios::sync_with_stdio(false)#define Pi acos(-1)#define pb push_back#define fi first#define se second#define db double#define ll long long#define ull unsigned long long#define Pir pair<int, int>#define m_p make_pair#define INF 0x3f3f3f3f#define esp 1e-7#define mod (ll)(1e9 + 7)#define for_(i, s, e) for(int i = (ll)(s); i <= (ll)(e); i ++)#define rep_(i, e, s) for(int i = (ll)(e); i >= (ll)(s); i --)#define sc scanf#define pr printf#define sd(a) scanf("%d", &a)#define ss(a) scanf("%s", a)
using namespace std;constint mxn =1e4;int dp[mxn];struct Node
{int p, q, w;} da[mxn];
bool cmp(Node a, Node b){return a.q - a.p < b.q - b.p;}intmain(){/* fre(); */int n, m;while(~sc("%d %d",&n,&m)){memset(dp,0,sizeof dp);for_(i,1, n){sc("%d %d %d",&da[i].p,&da[i].q,&da[i].w);}sort(da +1, da +1+ n, cmp);for_(i,1, n){rep_(j, m, da[i].q)
dp[j]=max(dp[j], dp[j - da[i].p]+ da[i].w);}pr("%d\n", dp[m]);}return0;}
K - 悼念 512 汶川大地震遇难同胞 —— 珍惜现在,感恩生活 HDU - 2191
思路
代码
#include<iostream>#include<algorithm>#include<cstring>
using namespace std;constint mxn =1e5;int dp[mxn];int n, m;int v, w, s;voidsov(int t){for(int j = m; j >= t * v; j --){
dp[j]=max(dp[j], dp[j - t * v]+ t * w);}}intmain(){int T;scanf("%d",&T);while(T--){memset(dp,0,sizeof dp);scanf("%d %d",&m,&n);for(int i =0; i <n; i ++){scanf("%d %d %d",&v,&w,&s);int t =1;while(s >= t){sov(t);
s -= t;
t <<=1;}if(s)sov(s);}printf("%d\n", dp[m]);}return0;}
L - 湫湫系列故事 —— 减肥记 I HDU - 4508
思路
代码
#include<iostream>#include<algorithm>#include<cstring>
using namespace std;constint mxn =1e5;int dp[mxn];int n, m;int v[mxn], w[mxn];intmain(){while(~scanf("%d",&n)){memset(dp,0,sizeof dp);for(int i =0; i <n; i ++)scanf("%d %d",&w[i],&v[i]);scanf("%d",&m);for(int i =0; i < n; i ++)for(int j = v[i]; j <= m; j ++)
dp[j]=max(dp[j], dp[j - v[i]]+ w[i]);printf("%d\n", dp[m]);}return0;}
思路
代码
M - Dividing HDU - 1059
思路
代码
#include<iostream>#include<algorithm>#include<cstring>
using namespace std;constint mxn =1e5;int dp[mxn];int n, m;int v[mxn], w[mxn];int s[mxn];voidsov(int t,int w){for(int j = m; j >= t * w; j --)
dp[j]=max(dp[j], dp[j - t * w]+ t * w);}intmain(){int cas =1;while(scanf("%d %d %d %d %d %d",&s[1],&s[2],&s[3],&s[4],&s[5],&s[6])){if(s[1]+ s[2]+ s[3]+ s[4]+ s[5]+ s[6]==0)break;memset(dp,0,sizeof dp);printf("Collection #%d:\n", cas ++);
m =0;for(int i =1; i <=6; i ++){
m +=(s[i]* i);};if(m %2){printf("Can't be divided.\n");printf("\n");continue;}
m /=2;for(int i =1; i <=6; i ++){int t =1;while(s[i]>= t){
s[i]-= t;sov(t, i);
t <<=1;}if(s[i])sov(s[i], i);}if(dp[m]== m)printf("Can be divided.\n");elseprintf("Can't be divided.\n");printf("\n");}return0;}
N - Piggy-Bank HDU - 1114
首先不难发现这一题是一个完全背包,但是这一题让求的是,通过合理的选择物品,使背包恰装满的最小价值是多少,我们将 转态转移方程中的 max 变成 min 就行了,
#include<iostream>#include<algorithm>#include<cstring>
using namespace std;constint mxn =1e5;struct Node
{int v, w;double x;} p[mxn];
bool cmp(Node a, Node b){return a.x > b.x;}int dp[mxn];int n, m;intmain(){int T;scanf("%d",&T);while(T --){int a, b;scanf("%d %d",&a,&b);
m = b - a;scanf("%d",&n);memset(dp,0x3f3f3f3f,sizeof dp);
dp[0]=0;for(int i =0; i < n; i ++){scanf("%d %d",&p[i].w,&p[i].v);
p[i].x =1.0* p[i].v / p[i].w;}for(int i =0; i < n; i ++)for(int j = p[i].v; j <= m; j ++){
dp[j]=min(dp[j], dp[j - p[i].v]+ p[i].w);}if(dp[m]<1e6){printf("The minimum amount of money in the piggy-bank is %d.\n", dp[m]);}elseprintf("This is impossible.\n");}return0;}