100 DP

100 DP NO CNBLOG

11.27 E. Generate a String

简单 dp 和 hhj 大佬 duel 的时候 duel 到这题了。这题虽然一眼 dp,但是我却没有出来简单的 dp 方式。赛时我想的是,先正向处理考虑添加和复制的操作。再反过来考虑删除的情况。

但其实超过后在进行删除的操作是完全多余的,在到达 fn 这个状态前就可以进行删除的操作,使他加上或复制刚好等于 n

2.28 P3413 SAC#1 - 萌数

思路简记:数位dp 回文数的处理不知道做,干脆单拎出来 moe用来记录该数是否有萌元素(回文数)
如何处理回文数 暴力枚举打表出所有回文数? 有点麻烦 而且位数有10000多位 →化简枚举 两位回文数,和三位回文数
如何处理最高位限制?

#include <bits/stdc++.h>
using namespace std;

const int N =1e4 ;

int f[N][10]; 
int num[N];

int dfs(int len,bool limit,bool lead,int moe) {
    if(len==0) {
        if(!lead&&moe)return 1;
        return 0;
    }
    if(!limit&&lead&&f[len][moe]!=-1) return f[len][moe];
    int maxx=limit?num[len]:9;
    int ans=0;
    for(int i=0;i<=9;i++) {
        ans+=dfs(len-1,limit&&(i==maxx),lead||i,moe);
    }
    if(moe==0) {
        int k2=0,k3=0;
        int x=num[len]*10+num[len-1];
        k2= num[len-1]>=num[len] ? ceil(x*1.0/10)-1 : x/10-1;
        if(num[len-1]==num[len]) {
        	k2--;
        	ans+=dfs(len-2,1,lead||i,1);
		}
		ans+=k2*dfs(len-2,0,lead||i,1);
    
        int a=num[len];
        int b=num[len-1];
        int c=num[len-2];
        k3=min(a,c)*b;
     //   ans+=k3*dfs(len-3,limit&&(i==maxx),lead||i,1);
    }

    ans=f[len][moe];
    return ans;
}
 
int work(string x, bool xx) {
    int len = 0;    
    for(int i=x.size();i>=0;i--) num[++len] = x[i]-'0';
    memset(f,-1,sizeof(f));
   	if(xx) num[1]--;
    return dfs(len,1,0,0);
}

int main() {
    ios::sync_with_stdio(0);
    string l,r;
    cin>>l>>r;
    cout<<work(r, 0)-work(l, 1);
    return 0;
}

(被题解提示后的我)

其实很简单,我们不是已经思考出了关于回文数的处理吗?只要考虑两位和三位就可以了。还有必要单独进行计算吗?只要在dfs() 的变量中加上上w1,w2 分别表示前一位,和前两位即可。这样,我们也不需要单独考虑回文数的limit的问题。实在是太妙啦。

还是需要继续提高思维能力捏。

2.27 P1725 琪露诺 -单调队列优化DP

看完题目很容易想到状态转移方程 设 fi 为到第 i 格的最大收益 fi=max(fi,fj+a[i]) (ir<=j<=il)

对于每个 fi 都是由 max(fj) 转移而来(ai 是一定的),我们每次都要转移 fi 时,都要回过头来寻找一个最大的 fj 这耗费了大量的时间。有没有什么办法可以优化掉寻找 max(fj) 的时间呢? 我们想到了熟悉的单调队列 在 O(n) 的条件下,寻找对于数组里的每一位寻找在它之前的最大值或最小值。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6;

int n, l, r, ans;
int a[N], f[N];
deque<int> q;

int main() {
	cin>>n>>l>>r;
	for(int i=0; i<=n; i++) cin>>a[i];
	memset(f,0xcf,sizeof(f));
	ans=f[0];f[0]=a[0];
	for(int i=l; i<=n; i++) {	
		int p=i-l;
		while(!q.empty()&&f[q.back()]<=f[p]) q.pop_back();
		q.push_back(p);
		while(!q.empty()&&q.front()<i-r) q.pop_front();
		f[i]=f[q.front()]+a[i];
	}
	for(int i=n+1-r; i<=n;i++)  ans=max(ans,f[i]);
	cout<<ans;
	return 0;
}

- 1.23 P1103 书本整理

题目简化 给定一个数列,和一个数字k,有k次机会将数列中的数字减一。求相邻差值之和最少。

其实如果考虑扔掉k本书,操作起来感觉非常的麻烦。如果考虑留下(n-k)书,再求差值是否会更简便呢?

f[i][j]=min(f[i][j],f[k][j-1]+abs(a[i]-a[k]));

考虑如何排书——
前i本书,选j本书。考虑摆在哪类书的下边。


- 1.178~1.19 P1564 膜拜

思路简述: 考虑前i个人分配机房,需要的最小机房数。再根据题目要求(要么保证整个机房都是同一位神牛的膜拜者,或者两个神牛的膜拜者人数差不超过 m)设计状态转移方程。

f[i]=min(f[i],f[j]+1)(abs(s1-s2)<=m||s1t||s2t)
//t为ij+1段的人数,s1为ij+1段膜拜牛1的人,s2为i~j+1段膜拜牛2的人


- 1.12 P3842 [TJOI2007] 线段

/*死掉的代码与死掉的思路
	f[i][j] 表示停在第i行,第j列,且完成改行的线段任务的最短路径
	f[i][j1]=min(f[i-1][j2]+cost,f[i][j1])
	cost为完成任务的话费,1<=j1<=n&&1<=j2<=n
但仔细思考后就会发现,cost很难求,并且没必要停留在非线段覆盖的点上*/
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e4;
int n,l[N],r[N],f[N][N];
int ans=0x3f3f3f3f;
int work(int i,int j1,int j2){
	int cost=1;
	int a=l[i],b=r[i];
	int s=min(j1,j2),t=max(j1,j2);
	if(s<=a) cost+=abs(b-s)+abs(b-t);
	else if(s<=b) cost+=abs(s-a)+abs(b-a)+abs(b-t);
	else cost+=abs(s-a)+abs(t-a);
	return cost;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
	
	memset(f,0x3f3f3f3f,sizeof(f));
	f[1][1]=0;
	for(int j=1;j<=n;j++){
		int cost=(j-1);
		if(j<=r[1]) cost+=(r[1]-j)*2;
		f[1][j]=cost;
	}
	for(int i=2;i<=n;i++){
		for(int j1=1;j1<=n;j1++){
			for(int j2=1;j2<=n;j2++){
				if(f[i][j1]>f[i-1][j2]){
					int tmp=f[i-1][j2]+work(i,j1,j2);
				//	if(f[i][j1]>tmp){
				///	cout<<i<<' '<<j1<<' '<<j2<<' '<<tmp<<' '<<work(i,j1,j2)<<endl;
				//	}
					f[i][j1]=min(f[i][j1],tmp);	
				}
				
			}
	////		cout<<f[i][j1]<<endl;
		///	if(i==n) ans=min(f[n][j1],ans);
		}
	}
///	cout<<ans;
	for(int j=1;j<=n;j++){
		ans=min(ans,f[n][j]);
	}
	cout<<ans;
	return 0;
}

思考简化掉一下无用的状态空间。
f[i][0/1]表示第i行的完成第i条线段,结束时站在左端点(0)右端点(1)的最优情况
下图是四种转移的情况。
四种cost计算情况


- 1.11 P1095 [NOIP2007 普及组] 守望者的逃离

二维DP

fi,j=max{fi1,j4fi1,j+7fi,j+10+60

二维dp 代码记录

一维DP

因为题目要求最短的时间,最长的路程。所以如果能使用魔法就尽量使用魔法。但是如果只使用魔法的话,会发现最后的时间里恢复的能量值是不足以使用魔法的,那休息还不如走两步路呢。于是我们就想到用 fi 来表示第i秒的走的的最长的路程。先处理好,如果只用魔法能走得最长路。在此基础上在处理走路的情况。
//不需要考虑先走路还是先用魔法的问题,因为无论顺序怎么样,到终点花费的时间是一样的。(无后效性)

#include<bits/stdc++.h>
using namespace std;
int m,s,t;
int f[1000006];
int main(){
	cin>>m>>s>>t;
	for(int i=1;i<=t;i++){
		if(m>=10){
			m-=10;
			f[i]=f[i-1]+60;
		}
		else{
			m+=4; f[i]=f[i-1];
		} 
	}
	for(int i=1;i<=t;i++){
		f[i]=max(f[i],f[i-1]+17);
		if(f[i]>=s){
			cout<<"Yes"<<endl<<i;
			return 0;
		}
	}
	cout<<"No"<<endl<<f[t];
	
	return 0;
}

贪心

贪心写法,烂尾了。。。

#include<bits/stdc++.h>
using namespace std;
int m,s,t;
int main(){
	cin>>m>>s>>t;
	int now=0;
	for(int i=1;i<=t;i++){
		if(m>=10){
			if(now+(m/10)*60>=s){
				while(now<s){
					now+=60;
					m/=10;
					i++;
					if(i==t) break;
				}
				cout<<"Yes"<<endl;
				cout<<i-1;
				return 0;
			}
			now+=min(m/10,t-i+1)*60;
			i+=m/10-1;
			m-=m/10*10;
		}
		else{
			if( ceil((s-now*1.0)/17)<(10-m)/4||
				( (ceil((s-now*1.0)/17)>t-i) &&(10-m)/4+(ceil(s-now*1.0)/60>t-i) ) ){
				now+=17;
				if(now>=s){
					cout<<"Yes"<<endl;
					cout<<i;
					return 0;
				}
			}
			else  m+=4;
		}
		cout<<i<<" "<<now<<" "<<m<<endl;
	}
	cout<<"No"<<endl;
	cout<<now;
	return 0;
}

作者:wh1sky

出处:https://www.cnblogs.com/wh1sky/p/17986327

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Wh1sky  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示