Description
Solution
这个题就是经典的最小点染色问题的模型。
对于弦图(题目中保证只有三角形,一定是弦图),可以按其完美序列(一个点的排列,使得每个点都是后缀的单纯点(邻居节点构成完全图的点))逆序依次染色,即可求得最小染色。
求完美序列可以用最大势能法(mcs),算法的过程为每次找出一个势能最大的点,放到当前序列的开头,并将不在序列中的该节点的邻居节点的势能增加,即可求出完美序列。正确性没有找到证明。使用链表(桶)可以做到(O(n+m))的复杂度。
由于求完美序列的顺序和染色顺序一致(逆序),所以可以同时进行这两个步骤。
Code
#include <cstdio>
#include <cstring>
#include <queue>
const int N = 10000 + 10;
const int M = 2000000 + 10;
int hd[N], to[M], nxt[M], cnt;
int col[N], flag[N], label[N], vis[N];
int lhd[N], lst[M], tot, w[M];
int ans, n, m;
inline void adde(int x, int y) {
cnt++;
to[cnt] = y;
nxt[cnt] = hd[x];
hd[x] = cnt;
}
inline void push(int x) {
tot++;
w[tot] = x;
lst[tot] = lhd[label[x]];
lhd[label[x]] = tot;
}
inline void mcs() {
int mxc = 0;
for (int i = 1; i <= n; ++i) push(i);
for (int i = n; i; --i) {
int cur = lhd[mxc];
while (flag[w[cur]]) {
lhd[mxc] = cur = lst[cur];
while (!cur) {
mxc--;
cur = lhd[mxc];
}
}
flag[w[cur]] = 1;
memset(vis, 0, sizeof vis);
for (int j = hd[w[cur]]; j; j = nxt[j]) {
if (!flag[to[j]]) {
if (++label[to[j]] > mxc) mxc = label[to[j]];
push(to[j]);
} else {
vis[col[to[j]]] = 1;
}
}
for (int j = 1; j <= n; ++j) if (!vis[j]) {
col[w[cur]] = j;
if (j > ans) ans = j;
break;
}
}
printf("%d
", ans);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
adde(x, y);
adde(y, x);
}
mcs();
return 0;
}
Note
开始刷八中的这两天,突然觉得自己会的算法太少了,有些算法即使会也难以应用,还是要努力学习,多刷题,理解更多的算法和模型,争取早日摆脱蒟蒻的行列。