NOIP做题记录

NOIP2015 提高组 子串

感觉是最长公共子序列模型的变式。

容易想到记 \(f[i][j][k]\) 表示 \(A\) 走到了第 \(i\) 位,\(B\) 匹配上了 \(1 \sim j\),目前分成了 \(k\) 段的方案数。

如果强制第 \(i\) 位必须匹配上的话,需要枚举位置 \(p\),满足 \(A[p] = B[j - 1]\)。这样的复杂度是 \(O(n^2m^2)\),无法通过本题。

我们采用类似最长公共子序列的方式,不必强制 \(i\) 必须匹配上,也可以直接从上一个状态继承过来。(优化 dp 定义)

具体来说,记 \(f[i][j][k][1/0]\) 表示 \(A\) 走到了第 \(i\) 位,\(B\) 匹配上了 \(1 \sim j\),目前分成了 \(k\) 段,\(a[i]\) 匹配 / 不匹配的方案数。

如果 \(a[i]\) 匹配上了,分段的话,上一位匹没匹配上无所谓,不分段的话,上一位也必须要匹配上,即:

\[f[i][j][k][1] = f[i - 1][j - 1][k - 1][0] + f[i - 1][j - 1][k - 1][1] + f[i - 1][j - 1][k ][1] \]

如果 \(a[i]\) 没匹配上,上一位匹没匹配上无所谓,即:

\[f[i][j][k][0] = f[i - 1][j][k][0] + f[i - 1][j][k][1] \]

答案就是 \(f[n][m][k][0] + f[n][m][k][1]\)。注意有上一位的继承,所以不用枚举谁匹配 \(b[m]\) 了。

时间复杂度 \(O(nm^2)\),而且跑不满,非常快。

算一下空间,\(128MB\) 存不下,把 \(i\) 维给滚掉就可以了。

(今天才发现原来 Luogu 不能 cin >> (a + 1),于是用了 scanf

#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=(r);++i)
#define G(i,r,l) for(int i(r);i>=(l);--i)
using namespace std;
using ll = long long;
const int mod = 1e9 + 7;
int f[2][205][205][2];
int n, m, s;
char a[1005], b[205];
signed main(){
	scanf("%d %d %d %s %s", &n, &m, &s, a + 1, b + 1);
	F(i, 1, n){
		f[(i - 1) & 1][0][0][0] = 1;
		F(j, 1, m) F(k, 1, s) f[i & 1][j][k][0] = f[i & 1][j][k][1] = 0;
		F(j, 1, min(i, m)){
			F(k, 1, min(j, s)){
				f[i & 1][j][k][0] = (f[(i - 1) & 1][j][k][0] + f[(i - 1) & 1][j][k][1]) % mod;
				if(a[i] == b[j]) 
					f[i & 1][j][k][1] = ((f[(i - 1) & 1][j - 1][k][1]+ f[(i - 1) & 1][j - 1][k - 1][0]) % mod + f[(i - 1) & 1][j - 1][k - 1][1]) % mod;
			}
		}
	}
	printf("%d", (f[n & 1][m][s][0] + f[n & 1][m][s][1]) % mod);
	return fflush(0), 0;
}

本题的关键在于打开思路,不必强制当前位必须匹配,一个小小的定义修改,足以影响复杂度。

NOIP2014 提高组 寻找道路

图论小思维题。

强烈推荐 这篇博客,当中对点的分类及进一步的思维过程非常清晰易懂,下文也有所引用。

所有点可分成三类:

  1. 普通点
  2. 自身与 \(t\) 连通的点
  3. 自身和所有出度都与 \(t\) 连通的点

题目所求:由第 3 类点构成的图上最短路径

如何建图:先由第 1 类点求出所有 2 类点,再由 2 类点求出所有 3 类点,具体实现为建反图,从 \(t\) 开始 dfs

一遍即可。

求答案:dijktra 或 bfs AC代码

NOIP2014 提高组 飞扬的小鸟

50pts 大概可以通过暴力建图跑最短路之类的方法完成。

70pts 是朴素 dp,记 \(f[i][j]\) 表示飞到 \((i, j)\) 的最小代价,由于上升阶段会枚举跳跃次数 \(k\),因此是 \(O(nm^2)\) 的,实际加上一些剪枝,卡到了 90pts。

100pts 是做一个背包,70pts 的上升阶段其实本质是个多重背包,但其实可以做成完全的,下降阶段就是 01背包。

细节写好一点的话代码里面分讨,判断比较多,但 400ms 就过了。 记录详情

参考了一下题解区的写法,可以把 inf 的也转移上,并且算 f[i + 1][m] 的时候算到 f[i + 1][2m],算完再合并,最后要清理一下撞到的柱子的部分,即 f[i + 1][1 ~ l[i + 1]]f[i + 1][r[i + 1] ~ m],重新设成 inf,这样的话很简洁,但跑了 600ms。 记录详情

NOIP2015 子串和 NOIP2014 飞扬的小鸟感觉都是优化 dp 定义和枚举,多余枚举的状态其实可以合并。通过优化循环顺序和稍微改动式子就能产生巨大的优化。

NOIP2021 数列

组合数 + 数位dp,50分是状压。dp深度好题(洛谷题解区有人说是NOIP中档题)

posted @ 2024-10-31 18:40  superl61  阅读(3)  评论(0编辑  收藏  举报