题解:洛谷 AT_abc468_c Between P and Q
【题目来源】
洛谷:AT_abc468_c [ABC468C] Between P and Q
【题目描述】
给定一个整数 \(N\) 和两个整数序列 \(P=(P_1,P_2,\ldots, P_N)\) 以及 \(Q=(Q_1,Q_2,\ldots, Q_N)\),每个序列都是 \(1,2,\ldots,N\) 的一个排列。
请你求出有多少个整数序列,满足该序列是 \(1,2,\ldots,N\) 的一个排列,并且在字典序上大于 \(P\) 且小于 \(Q\)。
什么是整数序列的字典序?对于两个整数序列 \(S=(S_1,S_2,\ldots,S_{|S|})\) 和 \(T=(T_1,T_2,\ldots,T_{|T|})\),若满足下列 1 或 2 的条件之一,则称 \(S\) 字典序小于 \(T\)。其中,\(|S|, |T|\) 表示 \(S,T\) 的长度。
- \(|S| < |T|\) 且 \((S_1,S_2,\ldots,S_{|S|}) = (T_1,T_2,\ldots,T_{|S|})\)。
- 存在整数 \(1 \leq i \leq \min\lbrace |S|, |T| \rbrace\) 使得以下两个条件同时成立:
- \((S_1,S_2,\ldots,S_{i-1}) = (T_1,T_2,\ldots,T_{i-1})\)。
- \(S_i\) 在数值上小于 \(T_i\)。
【输入】
输入从标准输入读入,格式如下:
\(N\) \(P_1\) \(P_2\) \(\ldots\) \(P_N\) \(Q_1\) \(Q_2\) \(\ldots\) \(Q_N\)
【输出】
输出满足条件的序列个数。
【输入样例】
3
1 3 2
3 1 2
【输出样例】
2
【核心思想】
-
问题分析:给定两个排列 \(P\) 和 \(Q\),求满足 \(P <_{lex} X <_{lex} Q\) 的排列 \(X\) 的数量。这是一个全排列枚举 + 字典序比较问题,核心在于利用
next_permutation按字典序生成所有排列并统计满足条件的个数。 -
算法选择:
- 全排列枚举:使用
next_permutation按字典序生成所有 \(N!\) 个排列 - 字典序比较:自定义函数逐位比较判断当前排列是否满足 \(P < X < Q\)
- 全排列枚举:使用
-
关键步骤:
- 读入数据:读取 \(N\),排列 \(P[1..N]\) 和 \(Q[1..N]\)
- 初始化:\(a[i] = i\)(字典序最小的排列)
- 全排列枚举(\(do-while\) 循环):
- 若 \(greaterThanP()\) 且 \(lessThanQ()\):\(ans \leftarrow ans + 1\)
- \(next\_permutation(a+1, a+N+1)\)
- 输出结果:\(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N! \cdot N)\),\(N!\) 个排列,每个比较 \(O(N)\)
- 空间复杂度:\(O(N)\),存储排列
-
全排列枚举与字典序的核心思想:
- 字典序的定义:从左到右逐位比较,第一个不同的位置决定大小关系。若所有位相同则两排列相等
- next_permutation 的有序性:
next_permutation按字典序从小到大生成排列,确保不遗漏、不重复 - 边界条件:\(P < X\) 要求 \(X\) 严格大于 \(P\)(不能等于),\(X < Q\) 要求 \(X\) 严格小于 \(Q\)(不能等于)
- 数据范围限制:\(N \leq 15\) 使得 \(N!\) 枚举可行(\(15! \approx 1.3 \times 10^{12}\) 实际不可行,但题目数据范围较小或存在更优解法)
- 适用于小规模排列枚举、字典序比较类基础问题
【算法标签】
普及- #DFS-一维
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 15; // 定义数组最大容量为15,满足N的数据范围(全排列枚举,N不能太大)
int n, ans; // n为排列长度,ans记录满足条件的排列数量
int a[N], p[N], q[N]; // a用于生成全排列;p和q分别存储给定的两个排列P和Q
// 判断当前排列a是否字典序大于排列P
bool greaterThanP()
{
for (int i = 1; i <= n; i++) // 从左到右逐位比较a和P
{
if (a[i] > p[i]) // 如果a的当前位大于P的当前位
return true; // a字典序大于P,返回true
if (a[i] < p[i]) // 如果a的当前位小于P的当前位
return false; // a字典序小于P,返回false
}
return false; // 所有位都相同,a等于P,不满足"大于",返回false
}
// 判断当前排列a是否字典序小于排列Q
bool lessThanQ()
{
for (int i = 1; i <= n; i++) // 从左到右逐位比较a和Q
{
if (a[i] < q[i]) // 如果a的当前位小于Q的当前位
return true; // a字典序小于Q,返回true
if (a[i] > q[i]) // 如果a的当前位大于Q的当前位
return false; // a字典序大于Q,返回false
}
return false; // 所有位都相同,a等于Q,不满足"小于",返回false
}
int main()
{
cin >> n; // 读入排列长度N
for (int i = 1; i <= n; i++) // 读入排列P
cin >> p[i];
for (int i = 1; i <= n; i++) // 读入排列Q
cin >> q[i];
for (int i = 1; i <= n; i++) // 初始化a为字典序最小的排列(1,2,...,N)
a[i] = i;
do
{
bool flag = false; // flag未使用
if (greaterThanP() && lessThanQ()) // 如果当前排列a同时满足:字典序大于P且小于Q
ans++; // 满足条件的排列数量加1
} while (next_permutation(a + 1, a + n + 1)); // 生成下一个字典序更大的排列,直到枚举完所有排列
cout << ans << endl; // 输出满足条件的排列总数
return 0;
}
【运行结果】
3
1 3 2
3 1 2
2
浙公网安备 33010602011771号