wtpavssite

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

目前对我来说比较难的题

由于这类题对现在的我来说比较吃力,所以我不太可能完全弄懂这道题为什么会这样想到,以及为什么会往这个方向做,所以我只会写出这道题的解决思路,和代码上的实现难点,可能还有一些我最开始砍到这道题的第一眼的思路。

1.https://ac.nowcoder.com/acm/contest/129231/F

这道题的思路区别于枚举每个分割方案,是枚举中间的每个子回文子串,计算出这段回文子串的贡献,也就是这段子串的左边的分割方案和右边的分割方案相乘,再×这个回文子串的价值,这样就可以把每个回文子串的总贡献加起来,得到所有分割方案的总价值。代码上的实现:
首先我们需要对所有的子串是不是回文的进行标记,我们可以观察到每个字符单独分割是一个回文子串,所以我们设置一个二维数组pal,x代表这个子串的起点,y代表截至的终点,所以对于每个pal的起始点都是自己的子串我们都需要设置成真,然后我们做进一步的初始化,也就是连续两个相邻的相同的字符组成的子串一定是回文的,那么我们的初始化的代码,就是`vector<vector>pal(n+2,vector(n+2,false));

for(int i=1;i<=n;i++){

pal[i][i]=true;

if(i<n&&s[i+1]==s[i])

pal[i][i+1]=true;

} 然后我们如何判断左端点l,右端点r的字符串是不是回文的,只需要看左端点l+1,右端点r-1的字符串是不是回文的,所以代码如下for(int len=3;len<=n;len++){

for(int l=1;l+len-1<=n;l++){

int r=l+len-1;

if(s[r]==s[l]&&pal[l+1][r-1])

pal[l][r]=true;

}

} 这步完了之后我们已经可以通过pal[i][j]来判断i到j的字符串是不是回文的字符串了,那么下一步就是如何计算1到i-1的字符串的分割方案,和l+1到n的分割方案。首先先给出代码吧vectorf(n+1);

f[0]=1;///代表字符串第一个到第i个的字符串可以的分割方案

for(int i=1;i<=n;i++){

for(int j=1;j<=i;j++){

if(pal[j][i]){

f[i]=(f[i]+f[j-1])%mod;

}

}

}
vectorg(n+2);///代表第i个字符到最后一个字符形成的字符串的分割方案数

g[n+1]=1;

for(int i=n;i>=1;i--){

for(int j=i;j<=n;j++){

if(pal[i][j]){

g[i]=(g[j+1]+g[i])%mod;

}

}

}`
这里面有些我还没怎么弄到彻底明白的问题。一个是为什么f和g的初始化需要弄到f[0]和g[n+1]解释说是因为不分割也是一种方案,所以把他们弄到两边去,然后f和g的dp上的实现是需要用到j-1,j+1的,也就是说如果我只设置f1=1,那么在i=1的时候,j=1,那么我们的f1加的f0是没有定义的,那么我们就需要在此基础上进行特殊判断,同理g也需要进行大量的特殊判断,而我们把f和g设置成0和n+1就不需要这么多的特殊判断。还有就是下一次再遇到类似的题我应该如何想到构建这个dp数组,已经如何想到使用dp来记录方案数,这道题其实最开始我根本想不到和dp是有关的
到这里我觉得最难得两个部分都已经解决了,剩下得就是计算每个回文子串得贡献,然后加起来,记得取模

2.https://pintia.cn/problem-sets/2033815669795565568/exam/problems/type/7?problemSetProblemId=2033815669829120010

这道题应该算是一个模板题,但是之前学关于树的很少,所以对于现在的我来说比较难。
这道题需要我们做两个事情,一个是根据给出的中序和后序重建出完整的二叉树;第二个是由完整的二叉树求出这棵树的右视图和左视图,实现分别是通过类似前序遍历的方法来实现。
代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e6;
int a[N];//中序
int b[N];//后序
vector<int>g[N];
int dfs_rebuild(int l1,int r1,int l2,int r2){
	if(l1>r1||l2>r2)
		return -1;
	int rt=b[r2];
	int R;
	for(int i=l1;i<=r1;i++){
		if(a[i]==rt){
			R=i-1;
			break;
		}
	}
	int l=dfs_rebuild(l1,R,l2,l2+(R-l1));
	int r=dfs_rebuild(R+2,r1,l2+(R-l1)+1,r2-1);
	if(l!=-1)g[rt].push_back(l);
	if(r!=-1)g[rt].push_back(r);
	return rt;
}
int mx=0;
void dfs_r(int node,int depth){
	a[depth]=node;
	mx=max(mx,depth);
	for(auto&u:g[node]){
		dfs_r(u,depth+1);
	}
}
void dfs_l(int node,int depth){
	a[depth]=node;
	mx=max(mx,depth);
	for(int i=int(g[node].size())-1;i>=0;i--){
		dfs_l(g[node][i],depth+1);
	}
}
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	int rt=dfs_rebuild(1,n,1,n);
	dfs_r(rt,1);
	cout<<"R: ";
	for(int i=1;i<=mx;i++){
		cout<<a[i]<<" \n"[i==mx];
	}
	dfs_l(rt,1);
	cout<<"L: ";
	for(int i=1;i<=mx;i++){
		cout<<a[i]<<" \n"[i==mx];
	}
}

3.Not Equal

https://ac.nowcoder.com/acm/contest/129582/E

虽然这道题我在比赛的时候并没有做,所以对于这道题的第一思路很少,赛后补题我也是跟着AI一步一步理解来补出来的。所以能将的很少,而且大概已经在题解里面写了,就不再赘述
题解链接https://www.cnblogs.com/wtpavssite/p/19735817

4.小红的点构造

来源:原题链接

这道题是我赛后补题跟着题解用AI做的,大概能理解里面每一步是在做什么,但是不确定下一次会不会写

代码:

里面的注释是我根据自己的理解写的

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
void solve() {
	int n, k;
	cin >> n >> k;
	vector<PII> P;///存进去的曼哈顿距离是1的点位
	vector<ll> E;///这个的back表示现在有多少个距离1
	P.pb({0, 0});///最开始从这个点开始存
	E.pb(0);
	int d = 1;///正方形的边长
	while (P.size() < n) {
		///先找上面的点(不包含角上)
		for (int x = 0; x < d && P.size() < n; ++x) {
			P.pb({x, d});
			E.pb(E.back() + (x == 0 ? 1 : 2));///除了原点正上方的那个点,其他的点都会和两个其他的点形成曼哈顿距离为1的距离,而相邻对就会+2
		}
		///找右边的点(不包含顶点)
		for (int y = 0; y < d && P.size() < n; ++y) {
			P.pb({d, y});
			E.pb(E.back() + (y == 0 ? 1 : 2));
		}
		///如果当前边长两边的点都进去了还是不够就加上顶点
		if (P.size() < n) {
			P.pb({d, d});
			E.pb(E.back() + 2);///顶点也是会形成距离2
		}
		d++;
	}
	if (E.back() < k) {///如果所有点总的距离也就是相邻对都不满足要求
		cout << "No" << endl;
		return;
	}
	cout << "Yes" << endl;
	int pos = 0;
	for (int i = 0; i < n; ++i) {
		if (E[i] <= k) {
			pos = i + 1;///pos找到第一个输出了的相邻对大于要求的位置
		}
	}
	for (int i = 0; i < pos; ++i) {
		cout << P[i].fi << " " << P[i].se << endl;
	}
	int r = k - E[pos - 1], rem = n - pos;
	///r代表还剩下多少相邻的没有输出
	///rem代表n里面还有多少对
	if (rem) {
		if (r == 1) {///由于E每次加上去都是1或者2,所以r只有0或1的情况
			int mx = -1, yy = -1;
			for (int i = 0; i < pos; ++i) {///找到现在x坐标最大的一个点,在他的右边加上一个点,刚好使相邻对加1
				if (P[i].fi > mx) {
					mx = P[i].fi;
					yy = P[i].se;
				}
			}
			cout << mx + 1 << " " << yy << endl;
			rem--;
		}
		int x = -1e8, y = -1e8;
		while (rem > 0) {///输出剩下的点位,让他们不相邻
			cout << x << " " << y << endl;
			x += 2;
			rem--;
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	t=1;
//	cin>>t;
	while(t--){
		solve();
	}
}

posted on 2026-03-12 14:02  wt_pav  阅读(14)  评论(0)    收藏  举报