一些DP杂题
1.
一道简单的背包,然而我还是写了很久QAQ
时间范围是都小于5 显然考虑一维背包,dp[i]表示目前A消耗了i的最小B消耗
注意
if(b[i]) dp[j]=dp[j]+b[i];
else dp[j]=1e9+7;
可以用B则直接转移,否则要把上一次的这个状态设为正无穷,只能用后两个转移。
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<queue>
- #include<vector>
- using namespace std;
- const int maxn=+;
- const int N=*+;
- int n,a[maxn],b[maxn],c[maxn],dp[N],lz[N],ans=1e9+;
- int main()
- {
- scanf("%d",&n);
- for(int i=;i<=n;i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);
- memset(dp,,sizeof(dp));
- dp[]=;
- for(int i=;i<=n;i++)
- for(int j=i*;j>=;j--){
- if(b[i]) dp[j]=dp[j]+b[i];
- else dp[j]=1e9+;//!!!!!!!!!!!!!!!!!!!!
- if(a[i]&&j>=a[i]&&dp[j]>dp[j-a[i]]) dp[j]=dp[j-a[i]];
- if(c[i]&&j>=c[i]&&dp[j]>dp[j-c[i]]+c[i]) dp[j]=dp[j-c[i]]+c[i];
- if(i==n) ans=min(max(dp[j],j),ans);
- }
- cout<<ans;
- return ;
- }
2.
看数据范围似乎是n^2可以过的,然而自己之前并不会写nlongn求最长上升子序列的算法就自己YY了一下,写得很丑,单调栈里从短到长从大到小,用了两个二分,一次找找最长的比它小的,一次找长度为它的位置是否可以更新。
这样找到以每个元素打头的最长上升序列,询问就从1到n跑一遍问它能不能到那么长,就保证了字典序最小。
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<stack>
- #include<vector>
- using namespace std;
- const int maxn=+;
- int now,n,m,x,maxx,a[maxn],pre[maxn],dp[maxn],sta[maxn],sl=,sr;
- int ef(int l,int r,int x){
- int res=-;
- while(l<=r){
- int mid=(l+r)>>;
- if(a[sta[mid]]>x) res=mid,l=mid+;
- else r=mid-;
- }
- return res;
- }
- void ef2(int l,int r,int len,int x){
- while(l<=r){
- int mid=(l+r)>>;
- if(dp[sta[mid]]==len) { if(a[sta[mid]]<=a[x]) sta[mid]=x; break;}
- if(dp[sta[mid]]<len) l=mid+;
- else if(dp[sta[mid]]>len) r=mid-;
- }
- }
- void work() {
- for(int i=n;i>=;i--) {
- dp[i]=;
- if(sl<=sr) {
- now=ef(sl,sr,a[i]);
- if(now!=-)
- dp[i]=dp[sta[now]]+;
- }
- maxx=max(maxx,dp[i]);
- while(sr>=sl&&dp[sta[sr]]<=dp[i]&&a[sta[sr]]<=a[i]) {
- sr--;
- }
- if(sr<sl||dp[sta[sr]]<dp[i]) sta[++sr]=i;
- else ef2(sl,sr,dp[i],i);
- }
- }
- void query(int x){
- if(x>maxx) puts("Impossible");
- else {
- int pre=;
- for(int i=;i<=n;i++) {
- if(dp[i]>=x&&a[i]>pre) {
- pre=a[i];
- if(x==)
- printf("%d",a[i]);
- else printf("%d ",a[i]);
- x--;
- if(!x) break;
- }
- }
- printf("\n");
- }
- }
- int main()
- {
- scanf("%d",&n);
- for(int i=;i<=n;i++) scanf("%d",&a[i]);
- work();
- scanf("%d",&m);
- for(int i=;i<=m;i++) {
- scanf("%d",&x);
- query(x);
- }
- return ;
- }
然而正确的nlogn求最长上升序列并不是这么写的,只用一个二分,不过在下并不是很清楚,懒得学以后再说吧。
从LLJ大佬那里学到了用线段树的做法,开一颗权值线段树,从后往前把Dp值存进去,每个点找它后面的最大Dp值来更新,感觉和正常的nlogn的思路可能差不多。
3.
二进制数和它们的位模式对于计算机程序员来说总是非常有趣的。 在这个
您需要计算具有以下属性的正数二进制数的问题:
•数字正好是N位宽,它们没有前导零。
•一和零的频率相等。
•数字是K的倍数。
输入
输入文件包含几个测试用例。 输入的第一行为您提供了测试用例数,
T(1≤T≤100)。 那么T测试用例会跟随,每一行都在一行。 每个测试用例的输入包括
的两个整数,N(1≤N≤64)和K(0≤K≤100)。
产量
对于每组输入,首先打印测试用例编号。 然后打印二进制数的数字
谷歌翻译神坑,1和0频率相同翻译成0和0频率相同,喵喵喵?
把Case打成case被坑了一波。。对拍才发现QAQ
最好的做法是往后加0或者1 不用特判,不会炸整,往前加的话就要特判,然后注意开 long long ,要模两次保证不会炸 (LLJ大佬说要开usinged long long ,因为 long long 只到2^64-1,实际这题只到2^63所以不用)
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<queue>
- #include<vector>
- using namespace std;
- typedef unsigned long long LL;
- const int maxn=;
- const int maxk=;
- int T,n,k;
- LL dp[maxn][maxn][maxk],ans,ll=;
- void work(){
- if(!k||n&) {printf("0\n"); return;}
- memset(dp,,sizeof(dp));
- dp[][][]=;
- for(int i=;i<=n;i++)
- for(int j=;j<=i;j++)
- for(int l=;l<k;l++) {
- if(j) dp[i][j][(l+((ll=)<<(i-))%k)%k]+=dp[i-][j-][l];
- if(i!=n) dp[i][j][l]+=dp[i-][j][l];
- }
- ans=;
- printf("%llu\n",dp[n][n/][]);
- }
- int main()
- {
- scanf("%d",&T);
- for(int i=;i<=T;i++){
- scanf("%d%d",&n,&k);
- printf("Case %d: ",i);
- work();
- }
- return ;
- }
这是往后加的版本
- for(int i=;i<n;i++)
- for(int j=;j<=i;j++)
- for(int l=;l<k;l++) {
- dp[i+][j+][((l<<)|)%k]+=dp[i][j][l];
- dp[i+][j][(l<<)%k]+=dp[i][j][l];
- }
4.
我爱记忆化搜索,记忆化搜索最强。
dp[i][j][o][k]表示i到j的订单已处理好,现在在i或者j 还要送k家的最优解
枚举,记忆化
- for(int i=;i<l;i++) u=max(u,dfs(i,r,,k-)+e[i]-k*abs(p[i]-p[o==?l:r]));
- for(int i=n;i>r;i--) u=max(u,dfs(l,i,,k-)+e[i]-k*abs(p[i]-p[o==?l:r]));
注意这一段先搜两边再中间,可以达到记忆化效果
代码
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<queue>
- #include<vector>
- using namespace std;
- const int maxn=+;
- int T,now,dp[maxn][maxn][][maxn],vis[maxn][maxn][][maxn],n,p[maxn],e[maxn],ans;
- int dfs(int l,int r,int o,int k) {
- if(k==) return ;
- if(vis[l][r][o][k]==now) return dp[l][r][o][k];
- vis[l][r][o][k]=now;
- int &u=dp[l][r][o][k];
- u=;
- for(int i=;i<l;i++) u=max(u,dfs(i,r,,k-)+e[i]-k*abs(p[i]-p[o==?l:r]));
- for(int i=n;i>r;i--) u=max(u,dfs(l,i,,k-)+e[i]-k*abs(p[i]-p[o==?l:r]));
- return u;
- }
- int main()
- {
- scanf("%d",&T);
- for(now=;now<=T;now++) {
- ans=;
- scanf("%d",&n);
- for(int i=;i<=n;i++) scanf("%d",&p[i]);
- for(int i=;i<=n;i++) scanf("%d",&e[i]);
- for(int kk=;kk<=n;kk++)
- for(int i=;i<=n;i++)
- ans=max(ans,dfs(i,i,,kk-)+e[i]-kk*abs(p[i]));
- printf("%d\n",ans);
- }
- return ;
- }
一开始傻逼地kmp直接往前跳一个就停了,后来改了又调了好久发现自己Kmp写错了,贼难受。。
就kmp转移然后矩阵优化。
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<queue>
- #include<vector>
- using namespace std;
- int ans,n,m,p,top,a[],nxt[],cnt[],vis[];
- struct jz{
- int x[][];
- friend jz operator *(const jz&l,const jz&r){
- jz res;
- for(int i=;i<;i++)
- for(int j=;j<;j++)
- res.x[i][j]=;
- for(int i=;i<;i++)
- for(int j=;j<;j++) {
- for(int k=;k<;k++)
- (res.x[i][j]+=(l.x[i][k]*r.x[k][j])%p)%=p;
- }
- return res;
- }
- }base,ret,tmp;
- void input() {
- scanf("%d%d%d",&n,&m,&p);
- char ch=getchar();
- while(ch<''||ch>'') ch=getchar();
- for(;ch>=''&&ch<='';ch=getchar()) a[++top]=ch-'';
- }
- int make_nxt(int a[],int *nxt) {
- for (int j=,i=;i<=m;i++) {
- while (j&&a[j+]!=a[i]) j=nxt[j];
- if (a[j+]==a[i]) j++;
- nxt[i]=j;
- }
- }
- void ksm(int b){
- while(b){
- if(b&) ret=ret*base;
- base=base*base;
- b>>=;
- }
- }
- void work() {
- for(int i=;i<m;i++) {
- cnt[i]++;
- vis[a[i+]]=i+;
- if(i+!=m) {base.x[i][i+]++;}
- int tp=i;
- while(nxt[tp]) {
- tp=nxt[tp];
- if(vis[a[tp+]]!=i+) {
- vis[a[tp+]]=i+;
- cnt[i]++;
- base.x[i][tp+]++;
- }
- }
- if(vis[a[]]!=i+) {
- cnt[i]++;
- base.x[i][]++;
- }
- base.x[i][]+=(-cnt[i]);
- }
- for(int i=;i<m;i++)
- for(int j=;j<m;j++)
- if(i==j) ret.x[i][j]=;
- ksm(n);
- ans=;
- for(int i=;i<m;i++)
- (ans+=ret.x[][i])%=p;
- printf("%d\n",ans);
- }
- int main()
- {
- input();
- make_nxt(a,nxt);
- work();
- return ;
- }
GT考试
在某位学长的博客里看到这题第一句话是省选怎么会考这么简单的题呢,然后就发现自己不会做。。。
神奇的单调队列优化,可能是我单调队列写得太少了。。
很容易知道状态dp[i][j]表示时间为i手中股票为j的最大收益,初始化为极小值,dp[0][0]=0;
然后转移分三种
不买不卖:dp[i][j]=max(dp[i][j],dp[i-1][j]);
买入:dp[i][j]=max(dp[i][j],dp[i-w-1][j-k]-k*ap[i]);
卖出:dp[i][j]=max(dp[i][j],dp[i-w-1][j+k]+k*bp[i]);
然后我们发现后两个枚举k会超时,就把它优化一下 (其实我也。。比较懵逼)
买入:dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i]);
=max(dp[i][j],(dp[i-w-1][k]+k*ap[i])-j*ap[i]);
卖出:dp[i][j]=max(dp[i][j],dp[i-w-1][k]+(k-j)*bp[i]);
=max(dp[i][j],(dp[i-w-1][k]+k*ap[i])-j*bp[i]);
以买入为例,我们发现 这一部分 dp[i-w-1][k]+k*ap[i] (j-k>0,j-k<as[i])具有单调性,
k>j-as[i] 我们枚举k,然后丢进单调队列, 更新现在的dp[i][k];
- //Twenty
- #include<cstdio>
- #include<cstdlib>
- #include<iostream>
- #include<algorithm>
- #include<cmath>
- #include<cstring>
- #include<queue>
- #include<vector>
- using namespace std;
- const int maxn=+;
- int ans,n,maxp,w,dp[maxn][maxn];
- int ap[maxn],bp[maxn],as[maxn],bs[maxn],que[maxn],ql=,qr;
- int main()
- {
- scanf("%d%d%d",&n,&maxp,&w);
- for(int i=;i<=n;i++)
- scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]);
- memset(dp,,sizeof(dp));
- dp[][]=;
- for(int i=;i<=n;i++) {
- ql=,qr=;
- for(int j=;j<=maxp;j++) {
- dp[i][j]=max(dp[i][j],dp[i-][j]);
- if(i-w->) {
- while(ql<=qr&&que[ql]<j-as[i]) ql++;
- while(ql<=qr&&dp[i-w-][que[qr]]+que[qr]*ap[i]-j*ap[i]<=dp[i-w-][j]) qr--;
- que[++qr]=j;
- if(ql<=qr) dp[i][j]=max(dp[i][j],dp[i-w-][que[ql]]+que[ql]*ap[i]-j*ap[i]);
- }
- else if(j<=as[i]) dp[i][j]=max(dp[i][j],-ap[i]*j);
- }
- ql=,qr=;
- for(int j=maxp;j>=;j--) {
- if(i-w->){
- while(ql<=qr&&que[ql]>j+bs[i]) ql++;
- while(ql<=qr&&dp[i-w-][que[qr]]+que[qr]*bp[i]-j*bp[i]<=dp[i-w-][j]) qr--;
- que[++qr]=j;
- if(ql<=qr) dp[i][j]=max(dp[i][j],dp[i-w-][que[ql]]+que[ql]*bp[i]-j*bp[i]);
- }
- }
- }
- for(int i=;i<=maxp;i++) ans=max(ans,dp[n][i]);
- printf("%d\n",ans);
- return ;
- }
股票交易
一些DP杂题的更多相关文章
- dp杂题(根据个人进度选更)
----19.7.30 今天又开了一个新专题,dp杂题,我依旧按照之前一样,这一个专题更在一起,根据个人进度选更题目; dp就是动态规划,本人认为,动态规划的核心就是dp状态的设立以及dp转移方程的推 ...
- 贪心/构造/DP 杂题选做Ⅱ
由于换了台电脑,而我的贪心 & 构造能力依然很拉跨,所以决定再开一个坑( 前传: 贪心/构造/DP 杂题选做 u1s1 我预感还有Ⅲ(欸,这不是我在多项式Ⅱ中说过的原话吗) 24. P5912 ...
- 贪心/构造/DP 杂题选做Ⅲ
颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...
- 专题:DP杂题1
A POJ 1018 Communication System B POJ 1050 To the Max C POJ 1083 Moving Tables D POJ 1125 Stockbroke ...
- 【做题记录】DP 杂题
P2577 [ZJOI2004]午餐 $\texttt{solution}$ 想到贪心: 吃饭慢的先打饭节约时间, 所以先将人按吃饭时间从大到小排序. 状态: \(f[i][j]\) 表示前 \(i\ ...
- 贪心/构造/DP 杂题选做
本博客将会收录一些贪心/构造的我认为较有价值的题目,这样可以有效的避免日后碰到 P7115 或者 P7915 这样的题就束手无策进而垫底的情况/dk 某些题目虽然跟贪心关系不大,但是在 CF 上有个 ...
- DP杂题2
1.邦邦的大合唱站队 https://www.luogu.org/problem/show?pid=3694 XY说这是道简单的签到题,然后我大概是普及组都拿不到三等的那种了.. 插入题解.写得太好了 ...
- 【模拟8.01】matrix(DP杂题,思维题)
很神的题,感谢lnc大佬的指点. 先设1-LL[i]统称左区间,RR[i]-m为右区间 用L[i]统计从1-i列,出现的左区间端点的前缀和,R[i]是右区间.... f[i][j]中j表示当前在第i列 ...
- 正睿OI DAY3 杂题选讲
正睿OI DAY3 杂题选讲 CodeChef MSTONES n个点,可以构造7条直线使得每个点都在直线上,找到一条直线使得上面的点最多 随机化算法,check到答案的概率为\(1/49\) \(n ...
随机推荐
- Python--前端之HTML
html概述和基本结构 HTML概述 HTML是 HyperText Mark-up Language 的首字母简写,意思是超文本标记语言,超文本指的是超链接,标记指的是标签,是一种用来制作网页的语言 ...
- .content和.text的区别
python中内置库 requests的两种方法get()和post()返回的的一个对象,有两种方法.content和.text ..content返回的是字节码,.text返回的是字符串.
- PHP基础知识总结(五) php面试题
1.Ajax跨域 json:数据交换格式,{"name":"wangtianle"} jsonp:非官方跨域数据交换协议,可以通过动态添加<script/ ...
- 声明式语法重写基于容器CICD构建流水线
调试了一下午,一句话都不想说了,看代码. ----- 参考文档 https://blog.csdn.net/weixin_42010394/article/details/90670945 实践代码 ...
- 262K Color
262K色=2^18=262144色. 320*240是指屏幕分辨率. 你可以理解为一块黑板,这款黑板宽是3.2M,长是2.4米,以1cm为最小单位,整个黑板被分为320*240个小格子,这个小格子里 ...
- 一个python练习
问题描述: 有一对兔子,每隔3个月就生一对兔子,生下来的兔子也是每隔3个月就生兔子,以此类推... 用python模拟出来: #!/usr/bin/python3 import random impo ...
- Struts1.3——使用MyEclipse集成工具开发Struts
前面两篇通过手工写代码,手工配置struts-config.xml文件的方法来开发了一个最简单的struts项目,通过手工的方式旨在学习底层原理细节.当熟悉了以后,我们就不需要再通过纯手工的方式来开发 ...
- Java DOM解析器 - 查询XML文档
这是需要我们查询的输入XML文件: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0"?> ...
- 洛谷 P2023 维护序列——线段树
先上一波题目 https://www.luogu.org/problem/P2023 复习了一波线段树 题目涉及的操作有区间加 区间乘以及区间求和 tips:线段树在传标记的时候 优先传乘法标记再传加 ...
- c# SqlBulkCopy实现批量从数据集中把数据导入到数据库中
今天遇到了一个导入类第一次见 SqlBulkCopy 可以实现从一个数据集导入到数据库中的表中 本来想从数据集中一条条遍历insert到库中 有了这个后发现: 只在把表与数据集的列做一下对应关系,再走 ...