前言
寒假集训考试题。当时甚至想到最小生成树上了。wtcl
题目大意
同学们要去参观。
同学们不能单独参观,必须结伴同行。为了公平,任意两名同学都要结伴去参观一次,即大家一共会进行 n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2次参观。
另外,由于同学们之间的亲密值不同,每个人都在心目中有一个与其他同学关系的排序,每个人都想按照这个顺序依次与每个人结伴参观。
请你帮忙计算,能否安排一个合适的参观顺序?以及最少需要多少天才能让完成同学们的参观计划?如果无法安排一个满足条件的参观计划,输出-1
3 ≤ n ≤ 1000 3 \leq n \leq 1000 3≤n≤1000
思路
首先,图论肯定看出来了。之后考虑建模,也是本题最难的地方。首先不能把一个人当成一个点,否则出来的感觉会很别扭。由于每一组人早晚都得参加,所以可以把每一组看成一个点。之后是拓扑排序,按照顺序进行拓扑即可。
每一组看成一个点有一个小技巧。就是用一个公式代表两个点。可以是 ( u − 1 ) ∗ n + v (u-1)*n+v (u−1)∗n+v,这样 1 , 1 1,1 1,1 就是 1 1 1, 1 , 2 1,2 1,2 就是 2 2 2,就不会重复了。这个可以根据题目去设计。
代码
#include<iostream>
#include<algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
const ll MAXN = 1e6;
const ll MAXM = 1e6;
struct edge {
ll to, nxt;
} e[MAXM];
ll head[MAXN], tot;
void add(ll u, ll v) {
e[++tot].nxt = head[u];
e[tot].to = v;
head[u] = tot;
}
ll n, ind[MAXN], dp[MAXN], ans;
ll node_id(ll u1, ll u2) {
if (u1 > u2) {
swap(u1, u2);
}
return (u1 - 1) * n + u2;
}
int main() {
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) {
ll a;
scanf("%lld", &a);
ll lid = node_id(i, a);
for (int j = 1; j < n - 1; ++j) {
scanf("%lld", &a);
ll id = node_id(i, a);
add(lid, id);
ind[id]++;
lid = id;
}
}
ll num = node_id(n - 1, n);
queue<ll> q;
for (int i = 1; i <= num; ++i) {
if (ind[i] == 0) {
q.push(i);
dp[i] = 1;
ans = 1;
}
}
ll cnt = 0;
while (!q.empty()) {
ll u = q.front();
q.pop();
cnt++;
for (ll i = head[u]; i; i = e[i].nxt) {
ll v = e[i].to;
ind[v]--;
dp[v] = max(dp[v], dp[u] + 1);
ans = max(ans, dp[v]);
if (ind[v] == 0) {
q.push(v);
}
}
}
if (cnt != num) {
printf("-1\n");
} else {
printf("%lld\n", ans);
}
return 0;
}
总结
本题的难点在于想到怎么去建模。其实图的结点不一定是每一个单独的点,其实结点是“状态”。有的时候状态只有一个,而有的时候多个数才能组成一个状态。