题解:AtCoder AT_abc468_c Between P and Q
【题目来源】
AtCoder:Between P and Q
【题目描述】
You are given an integer \(N\) and integer sequences \(P=(P_1,P_2,\ldots, P_N)\) and \(Q=(Q_1,Q_2,\ldots,Q_N)\), each of which is a permutation of \((1,2,\ldots,N)\).
Find the number of integer sequences that are a permutation of \((1,2,\ldots,N)\) and are lexicographically greater than \(P\) and lexicographically less than \(Q\).
What is lexicographic order for integer sequences?
For integer sequences \(S = (S_1,S_2,\ldots,S_{|S|})\) and \(T = (T_1,T_2,\ldots,T_{|T|})\), we say that \(S\) is lexicographically smaller than \(T\) if \(1.\) or \(2.\) below holds. Here, \(|S|, |T|\) denote the lengths of \(S, T\), respectively.
- \(|S| \lt |T|\) and \((S_1,S_2,\ldots,S_{|S|}) = (T_1,T_2,\ldots,T_{|S|})\).
- There exists an integer \(1 \leq i \leq \min\lbrace |S|, |T| \rbrace\) such that both of the following two conditions hold.
- \((S_1,S_2,\ldots,S_{i-1}) = (T_1,T_2,\ldots,T_{i-1})\)
- \(S_i\) is (numerically) smaller than \(T_i\).
给定一个整数 \(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| \lt |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\)。
【输入】
The input is given from Standard Input in the following format:
\(N\)
\(P_1\) \(P_2\) \(\ldots\) \(P_N\)
\(Q_1\) \(Q_2\) \(\ldots\) \(Q_N\)
【输出】
Output the answer.
【输入样例】
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号