标签:
1.6049:买书
小明手里有n元钱全部用来买书,书的价格为10元,20元,50元,100元。
问小明有多少种买书方案?(每种书可购买多本)
样例输入1: 20 样例输入2: 15 样例输入3: 0
/*一个求:恰好放满的完全背包的方案总数的问题,注意特判n==0就可以了*/ #include<cstdio> #include<iostream> using namespace std; #define N 1001 int n; long long int f[N]; int main() { scanf("%d",&n); int a[5]; a[1]=10;a[2]=20;a[3]=50;a[4]=100; f[0]=1; for(int i=1;i<=4;++i) for(int j=a[i];j<=n;++j) f[j]+=f[j-a[i]]; if(n==0) { printf("0\n"); return 0; } cout<<f[n]<<endl; return 0; }
专题二:参数过大的背包问题的转化(带有余数问题的DP)
1.2989:糖果
5 7 1 2 3 4 5
14
/*问题分析: 方法一:朴素01背包算法:总重量/作为参数,可以用bool判断,能不能达到某个重量 , 再用贪心倒叙找7的倍数就可以了,但是时间复杂度是n*10000,会超时间 方法二;把sum这个量由参数变为函数值,选择另一个与k有关的作为参数,那就是余数。 状态转移方程:f[i][j]=max(f[i][j],max(f[i-1][j],f[i-1][(k+j-a[i]%k)%k]+a[i])); f[i][j]表是,前i个物品,%k是j的最大糖果数目, 它包括两种情况:1.不取第i个包,那就是 f[i-1][j], 2.取第i个包,那么就要判断能转移到f[i][j]的前一种状态是什么, f[i-1][(k+j-a[i]%k)%k], 由题目的意思,假设 f[i][j]是由f[i-1][x]转移过来的,那么j=(x+a[i]%k)%k, 根据这个公式,可以得出x是 (k+j-a[i]%k)%k,括号里加一个k的原因,是为了防止 j-a[i]%k小于0的情况。 初始化: memset(f,-128,sizeof(f)); f[0][0]=0;注意这里区别好糖果是0,就是没取包,可以达到余数是0, 而取包之后达不到的状态赋值为-INF,防止出现某个糖果数,是由不成立的状态转移来的。 f[1][0]=0; f[1][a[1]%k]=a[1]; 最后输出;f[n][0] 就可以了。 */ #define INF 100000*100 #include<cstdio> #include<iostream> using namespace std; #include<cstring> #define N 101 int f[N][N],n,k,a[N]; void input() { scanf("%d%d",&n,&k); for(int i=1;i<=n;++i) scanf("%d",&a[i]); memset(f,-128,sizeof(f)); f[0][0]=0; f[1][0]=0; f[1][a[1]%k]=a[1]; } void DP() { for(int i=2;i<=n;++i) for(int j=0;j<=k-1;++j) f[i][j]=max(f[i][j],max(f[i-1][j],f[i-1][(k+j-a[i]%k)%k]+a[i])); } int main() { input(); DP(); if(f[n][0]>0) printf("%d\n",f[n][0]); else printf("0\n"); return 0; }
2.codevs 数字三角形W(与余数有关)
但是数字三角形题目是
数字三角形
要求走到最后mod 100最大
如果把每一层作为状态,这一层mod100如果取最大值对于下一层不一定是最优值,所以DP方程比较难写,n==25,规模不大,可以用深搜的方法
3.NOi 判断整除
3531:判断整除
一个给定的正整数序列,在每个数之前都插入+号或-号后计算它们的和。比如序列:1、2、4共有8种可能的序列:
(+1) + (+2) + (+4) = 7
(+1) + (+2) + (-4) = -1
(+1) + (-2) + (+4) = 3
(+1) + (-2) + (-4) = -5
(-1) + (+2) + (+4) = 5
(-1) + (+2) + (-4) = -3
(-1) + (-2) + (+4) = 1
(-1) + (-2) + (-4) = -7
所有结果中至少有一个可被整数k整除,我们则称此正整数序列可被k整除。例如上述序列可以被3、5、7整除,而不能被2、4、6、8……整除。注意:0、-3、-6、-9……都可以认为是3的倍数。
3 2 1 2 4
/*f[i][j]:bool类型数组判断 前i个数到达余数是j的情况能不能达到。 如何处理负数:f[i][j]=f[i][j]||f[i-1][(k+j-a[i]%k)%k]||f[i-1][(j+a[i]%k)%k], 负数当做正数处理,因为后面的数一起变符号,可以得到相同的状态(也就处理了负数可以整除的情况) */ #include<iostream> using namespace std; #include<cstdio> #include<cmath> #include<cstdlib> #define N 10001 #define K 101 bool f[N][K]; int n,k; int a[N]; void input() { scanf("%d%d",&n,&k); for(int i=1;i<=n;++i) scanf("%d",&a[i]); f[0][0]=true; for(int i=1;i<=n;++i) for(int j=0;j<=k-1;++j) f[i][j]=f[i][j]||f[i-1][(k+j-a[i]%k)%k]||f[i-1][(j+a[i]%k)%k]; if(f[n][0]) printf("YES\n"); else printf("NO\n"); } int main() { input(); return 0; }
专题三:最长公共上升子序列加求输出序列
NOI 2000:最长公共子上升序列
5 1 4 2 5 -12 4 -12 1 2 4
#include<iostream> using namespace std; #define N 501 #include<cstdio> #include<cstring> struct Xl{ int len; int ans[N]; }; Xl xl[N],now; int a[N],b[N],lena,lenb; void input() { scanf("%d",&lena); for(int i=1;i<=lena;++i) scanf("%d",&a[i]); scanf("%d",&lenb); for(int i=1;i<=lenb;++i) scanf("%d",&b[i]); } void DP() { for(int i=1;i<=lenb;++i) { now.len=0; memset(now.ans,0,sizeof(now.ans)); for(int j=1;j<=lena;++j) { /*xl[j]的含义:到当前循环的1--i区间以a[j]结尾的最长公共上升子序列的长度*/ if(a[j]<b[i]&&xl[j].len>now.len)/*因为只有a[j]<b[i],才把now更新,可以保证是上升的*/ { now=xl[j]; } if(a[j]==b[i])/*如果不相等,那么xl[j]还是之前的某个b[i]更新过来的,与当前的b[i]就没有关系了*/ { xl[j]=now;/*始终记录着now的最大值,可以减少最大值的寻找,减少一重循环*/ xl[j].len++; xl[j].ans[xl[j].len]=a[j]; } } } } int main() { input(); DP(); int p=0; int maxx=-N; for(int i=1;i<=lena;++i)/*因为f[i]储存着是a[i]结尾的序列,所以f[lena]不一定是最优值,所以要把所有以a[i]为结尾都循环走一边*/ { if(xl[i].len>maxx) { maxx=xl[i].len; p=i; } } printf("%d\n",xl[p].len); for(int i=1;i<=xl[p].len;++i) printf("%d ",xl[p].ans[i]); printf("\n"); return 0; }
标签:
原文地址:http://www.cnblogs.com/c1299401227/p/5349041.html