首页 > 其他分享 >Living-Dream 系列笔记 第49期

Living-Dream 系列笔记 第49期

时间:2024-03-09 22:14:16浏览次数:21  
标签:Living code 49 int len 枚举 Dream 转移 dp

T1

令 \(dp_{i,j}\) 表示卖出区间 \([i,j]\) 能获得的最大价值。

显然答案为 \(dp_{1,n}\)。

因为只能卖 \(i\) / \(j\),所以有转移:

\[dp_{i,j}=\max(dp_{i+1,j}+v_i \times (n-len+1),dp_{i,j-1}+v_j \times (n-len+1)) \]

初始:\(dp_{i,i}=v_i \times n\),其余为 \(- \infty\)。

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

const int N=2e3+5;
int n;
int v[N];
int dp[N][N];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>v[i];
	memset(dp,0xcf,sizeof(dp));
	for(int i=1;i<=n;i++) dp[i][i]=v[i]*n;
	for(int i=2;i<=n;i++){
		for(int j=1;j+i-1<=n;j++){
			int s=j,e=j+i-1;
			dp[s][e]=max(dp[s][e-1]+v[e]*(n-i+1),dp[s+1][e]+v[s]*(n-i+1));
		}
	}
	cout<<dp[1][n];
	return 0;
}

T2

我们注意到,对于字符串中的一个非对称位置 \(i\),

在对应位置添加 \(s_i\) 或删除 \(s_i\) 是等价的。

于是对于每个字符,它的操作代价即为 \(\min(\)添加代价\(,\)删除代价\()\)。

然后这题显然是个区间 dp。

因此有状态:令 \(dp_{i,j}\) 表示 将 \([i,j]\) 改为回文串的最小代价,答案为 \(dp_{1,m}\)。

接着我们发现这题显然不能用枚举中转点的套路,

因为即使 \([i,k]\) 与 \([k+1,j]\) 均为回文串,也不能保证 \([i,j]\) 为回文串。

于是我们考虑对端点进行转移,

具体来说,我们有:

\[dp_{i,j}=\max(dp_{i+1,j}+v_{s_i},dp_{i,j-1}+v_{s_j}) \]

因为我们是从短到长枚举区间,所以此转移的正确性可以保证。

同时,若 \(s_i=s_j\),则继承中间的 \(dp_{i+1,j-1}\) 即可。

在这种情况下,若区间长度 \(i \le 2\),则直接令 \(dp_{i,j}=0\) 即可。

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

const int N=2e3+5;
int n,m;
string t;
int v[N];
int dp[N][N];

int main(){
	cin>>n>>m>>t,t='#'+t;
	for(int i=1;i<=n;i++){
		char c; int x,y;
		cin>>c>>x>>y;
		v[c]=min(x,y);
	}
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=m;i++) dp[i][i]=0;
	for(int i=2;i<=m;i++){
		for(int j=1;j+i-1<=m;j++){
			int s=j,e=j+i-1;
			if(t[s]==t[e]){
				if(i==2) dp[s][e]=0;
				else dp[s][e]=dp[s+1][e-1];
			}
			dp[s][e]=min(dp[s][e],min(dp[s][e-1]+v[t[e]],dp[s+1][e]+v[t[s]]));
		}
	}
	cout<<dp[1][m];
	return 0;
}

T3

与上题类似,此处略。

T4

一眼区间 dp。

令 \(dp_{i,j}\) 表示 \([i,j]\) 能折叠出的最小长度。

答案即为 \(dp_{1,n}\)(\(n\) 表示 \(S\) 的长度)。

初始状态显然有 \(dp_{i,i}=1\),其余为 \(\infty\)。

对于转移,我们进行分类讨论:

  • 若不折叠,直接拼接,则按照枚举中转点套路即可。有转移:

\[dp_{i,j}=\min(dp_{i,j},dp_{i,k}+dp_{k+1,j}) \]

  • 否则若进行折叠,则重复部分的长度必为当前区间长度 \(len\) 的因数。

    于是我们从 \(1\) 至 \(\frac{len}{2}\)(因为枚举到 \(len\) 就相当于情况 1)去枚举 \(len\) 的因数 \(l\),

    并检查其是否满足要求。若满足,则有转移:

\[dp_{i,j}=dp_{i,i+l-1}+c(\frac{len}{l})+2 \]

(其中 \(c(\frac{len}{l})\) 表示 \(\frac{len}{l}\) 的位数,即题中的 \(X\);\(2\) 表示两个括弧)

直接转移即可。

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

const int N=1e2+5;
int m;
string t;
int v[N];
int dp[N][N];

bool check(int l,int r,int k){
	string o="";
	for(int i=l;i<=r;i+=k){
		if(i==l) o=t.substr(i,k);
		else
			if(t.substr(i,k)!=o) return 0;
	}
	return 1;
}
int c(int x){
	int p=0;
	while(x) p++,x/=10;
	return p;
}

int main(){
	cin>>t,t='#'+t,m=t.size()-1;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=m;i++) dp[i][i]=1;
	for(int i=2;i<=m;i++){
		for(int j=1;j+i-1<=m;j++){
			int s=j,e=j+i-1;
			for(int k=s;k<e;k++) dp[s][e]=min(dp[s][e],dp[s][k]+dp[k+1][e]);
			for(int l=1;l<=i/2;l++)
				if(!(i%l)&&check(s,e,l))
					dp[s][e]=min(dp[s][e],dp[s][s+l-1]+c(i/l)+2);
		}
	}
	cout<<dp[1][m];
	return 0;
}

作业 T1

令 \(dp_{i,j}\) 表示删去区间 \([i,j]\) 能获得的最大价值,答案即为 \(dp_{1,n}\)。

显然有初始状态令 \(dp_{i,i}=x_i\),其余为 \(-\infty\)。

对于 \([i,j]\),我们可以直接全删,于是先令 \(dp_{i,j}=\lvert x_i-x_j \rvert \times (j-i+1)\)。

然后按枚举中转点套路转移即可。

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

const int N=1e2+5;
int n,a[N],dp[N][N];

int main(){
	cin>>n,memset(dp,0xcf,sizeof(dp));
	for(int i=1;i<=n;i++) cin>>a[i],dp[i][i]=a[i];
	for(int i=2;i<=n;i++){
		for(int j=1;j+i-1<=n;j++){
			int s=j,e=j+i-1;
			dp[s][e]=abs(a[s]-a[e])*(e-s+1);
			for(int k=s;k<e;k++)
				dp[s][e]=max(dp[s][e],dp[s][k]+dp[k+1][e]);
		}
	}
	cout<<dp[1][n];
	return 0;
}

作业 T2

因为题中只含有两端点操作,所以考虑对端点进行转移。

具体地:

令 \(dp_{i,j,0}\) 表示区间 \([i,j]\) 中 \(i\) 从左边进的的初始队列方案数;

令 \(dp_{i,j,0}\) 表示区间 \([i,j]\) 中 \(j\) 从右边进的的初始队列方案数。

答案即为 \((dp_{1,n,0}+dp_{1,n,1}) \bmod 19650827\)。

然后显然有初始状态令 \(dp_{i,i,0}=1\),

这里默认第一个人从左进,避免重复计算。

接着考虑到第 \(i\) 人从左进时,它前面的人只可能为 \(i+1\) 或 \(j\),

因此它必须满足 \(h_i<h_{i+1}\) 或 \(h_i<h_j\) 才能进行转移,

易得此时转移方程:

\[dp_{i,j,0}=dp_{i,j,0}+dp_{i+1,j,0}(h_i<h_{i+1}) \]

\[dp_{i,j,0}=dp_{i,j,0}+dp_{i+1,j,1}(h_i<h_j) \]

同理,易得第 \(j\) 人从右进时的转移方程:

\[dp_{i,j,1}=dp_{i,j,1}+dp_{i,j-1,0}(h_j>h_{j-1})\\ \]

\[dp_{i,j,1}=dp_{i,j,1}+dp_{i,j-1,1}(h_j>h_i) \]

然后直接转移即可。注意取模。

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

const int N=1e3+5,MOD=19650827;
int n,a[N],dp[N][N][2];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],dp[i][i][0]=1;
	for(int i=2;i<=n;i++){
		for(int j=1;j+i-1<=n;j++){
			int s=j,e=j+i-1;
			if(a[s]<a[s+1]) dp[s][e][0]+=dp[s+1][e][0];
			if(a[s]<a[e]) dp[s][e][0]+=dp[s+1][e][1];
			if(a[e]>a[e-1]) dp[s][e][1]+=dp[s][e-1][1];
			if(a[e]>a[s]) dp[s][e][1]+=dp[s][e-1][0];
			dp[s][e][0]%=MOD,dp[s][e][1]%=MOD;
		}
	}
	cout<<(dp[1][n][0]+dp[1][n][1])%MOD;
	return 0;
}

标签:Living,code,49,int,len,枚举,Dream,转移,dp
From: https://www.cnblogs.com/XOF-0-0/p/18062328

相关文章

  • 491. 非递减子序列c
    复试的人真的搞心态啊,怎么能这么变态,刷题这么块。哭了。要是难一点的重复问题还是写for循环好点。/***Returnanarrayofarraysofsize*returnSize.*Thesizesofthearraysarereturnedas*returnColumnSizesarray.*Note:Bothreturnedarrayand*columnSi......
  • 8000MHz高频内存也赢不了AMD!锐龙7 7800X3D VS. i9-14900K网游与单机游戏性能对比
    一、前言:i9-14900K配8000MHz内存能否战胜锐龙77800X3D如今的Intel似乎有些魔怔,为了冲击高频而不顾一切。此前i9-14900K的满载功耗已经高达360W,而即将到来的i9-14900KS据闻峰值功耗已经超过400W,频率也来到了前所未有6.2GHz。与之形成强烈反差的是AMD的锐龙77800X3D,这款当前游戏......
  • Living-Dream 系列笔记 第2期
    本期主要讲解vector、map两个STL容器。知识点:首先,引入两种数组的区别:静态数组,指提前声明需要多少内存的数组,是连续的;而动态数组则是在插入元素时临时指定存储空间,不要求连续。STLvector是一个动态数组,下标默认从\(0\)开始。它支持的操作如下:定义:一维vector......
  • Living-Dream 系列笔记 第3期
    本期主要讲解二分查找。知识点二分查找:思想:分治。使用场景:在一个有序序列中,反复查找不同目标。时间复杂度:\(O(n\logn)\)。实现:对数列排序;确定二分边界(通常为L=最小下标-1,R=最大下标+1);伪代码:intL=左边界-1,R=右边界+1;while(L+1<R){intmid=(L+R)......
  • Living-Dream 系列笔记 第1期
    本期主要讲解模拟、枚举算法。例题T1简单模拟题。利用scanf/cin以int形式读入分和秒,并令秒循环累加,逢\(60\)归\(0\)并向分进\(1\),分则是逢\(24\)归\(0\)。在循环的过程中若分秒合起来是回文数字,则退出循环,按照题目格式输出当前时间。注意开始时间不算。#includ......
  • Living-Dream 系列笔记 第17期
    ProblemT1/*思路:分类讨论:若k=0,则输出x+1;若k>tot(x的位数),则输出1+k-tot个0+x;否则输出10^k+x。*/#include<bits/stdc++.h>usingnamespacestd;longlongk,x,tot,ans=1;//开longlongintmain(){ cin>>k>>x; if(k==0){cout<<x+1;return0;}//情况1......
  • Living-Dream 系列笔记 第14期
    本期主要讲解差分技巧。知识点我们令原数组为\(a_i\),则当且仅当\(d_i=a_i-a_{i-1}\)时,我们称\(d_i\)是\(a_i\)的差分数组。特别的,\(d_1=0\),\(d_{n+1}=-n\)。差分数组\(d_i\)有以下三个性质:\(d_i\)的前缀和数组为\(a_i\)。将\(a_{L\simR}\)这个区间统一加......
  • Living-Dream 系列笔记 第15期
    模拟赛爆炸祭。T1把所有连通块依次求出,若某个连通块的数量已经出现过,则说明它与以前的连通块属于同一星系,直接将星系大小加上连通块大小并取\(\max\);否则将星系数量\(+1\)。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intn,m;intans=-1e9,num,......
  • Living-Dream 系列笔记 第11期
    本期主要讲解与上期相同内容(雾。例题T1在整个矩阵外加一圈\(0\),使得包围圈外的\(0\)形成一整个连通块。求出这个连通块并标记为\(1\),然后输出即可。#include<bits/stdc++.h>usingnamespacestd;intn;intdx[]={-1,0,1,0},dy[]={0,1,0,-1};inta[31][31],g[31][31];......
  • Living-Dream 系列笔记 第12期
    本期主要讲解一维前缀和技巧。知识点我们令\(a_i\)表示原数组的第\(i\)个元素,则\(sum_i\)表示\(a_i\)前\(i\)个元素之和,即:\[sum_i=\sum^{i}_{j=1}a_j\]我们知道,\(a\)数组前\(i\)个元素的和\(=\)前\(i-1\)个元素的和\(+a_i\)。于是便可得到\(sum\)数组的......