CF316B1 EKG
题意:
一个医院有n个人排了一些队伍,每个人编号从1~n依次排列。一个人只知道站在他前面人的编号,有一些人忘记了自己前面人的编号。现在给出n个人和队伍中一个人的编号pos,要求依次输出编号为pos的人在队伍中所有可能站的位置。(队伍中不可能有环)
思路:
因为明确给出队伍不会成环,则发现每个队伍的尽头总会是0,所以利用dfs搜索把每个队伍的长度搜索出来。找的过程中特别记录点pos在所在队伍的位置。因为多个队伍所构成的链的长度组成方式有多种,这刚好与01背包的原理完全相同,所以使用动态规划枚举每一种情况。最终输出即可。该题最终时间复杂度为O(n2)
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <functional>
#include <set>
#define int long long
#define MAXN 2000
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
int arr[MAXN];
int arr2[MAXN];
bool vis[MAXN];
int flag[MAXN];
int val;
bool ff = 0;
int cnt;
//深搜查找链的长度
int dfs(int n,int pos,int cnt1) {
//若找到了点pos所在的位置则不需要继续向下寻找,直接记录点pos在这个链到第一个人的长度即可
if (n == pos) {
ff = 1;
val = cnt1 + 1;
return 0;
}
else if (n==0) {
cnt = cnt1;
return 0;
}
++cnt1;
dfs(arr[n],pos,cnt1);
return 0;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n, pos;
int k = 0;
int kk = 0;
cin >> n >> pos;
for (int i = 1; i <= n; ++i) {
cin >> k;
if (k!=0) {//记录第k个人前面的人i
arr[k] = i;
vis[i] = 1;
}
}//遍历一遍0的链,并找到链的长度。
for (int i = 1;i<=n;++i) {
ff = 0;
if (vis[i]==0) {
dfs(i, pos, 0);
if (ff==0) {
arr2[++kk] = cnt;
}
}
}
//找到所有链的长度后,找到所有链的组合方式,记录每种组合下链的长度
//此方法和01背包方法完全相同
flag[0] = 1;
for (int i = 1;i<=kk;++i) {//一维空间压缩
for (int j = n;j>=arr2[i];--j) {
flag[j] = max(flag[j - arr2[i]], flag[j]);
}
}
for (int i = 0;i<=n;++i) {//遍历所有情况
if (flag[i]==1) {
cout << i+val << endl;
}
}
return 0;
}