对一个图进行染色,要求每一条边相连的两个点都是异色的。求所用到颜色的最小数量。
此题保证图为一个弦图(周长>=4的环中间一定有一条弦),使用最大势算法。
最大势:
最初所有点的值都是0。做n次,每次找出值最大的一个点p(如果有多个就任意),把与之相连的点都+1,然后删除p(p的值要保留)。
做完后,答案就是所有点的分值中出现过的不同的数字的个数。
由于复杂度太大,必须要优化。考虑到最多分值到n-1,于是建一个deque(网上说是链表,但是链表好难写的,原理相同就行),deque[i]存储——值为i的所有点的编号。
每次更新点时只需要把这个新的点加入到另一个队列即可,并不需要删点。因为我们在选取当前点now的时候,会把那些已经遍历过的点pop掉。(类比dijkstra)
维护一个best保留目前的最大分值,如果把deque[best]都pop空了,那么--best;
上代码:
#include <cstdio>
#include <deque>
#include <algorithm>
#include <cstring>
#define N 11000
#define M 1100000
using namespace std;
struct EDGE {
int to, next;
}edge[2*M];
bool v[N];
int head[N], num[N];
int n, m, cnt;
inline int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = getchar();}
return x * f;
}
inline void add(int u, int v) {
edge[++cnt].to = v;
edge[cnt].next = head[u]; head[u] = cnt;
return ;
}
inline void solve() {
deque<int>Q[N];
memset(num, 0, sizeof(num));
memset(v, false, sizeof(v));
int best = 0;
for(int i = 1; i <= n; ++i) Q[0].push_back(i);
int ans;
while(1) {
while(!Q[best].empty() && v[Q[best].front()]) Q[best].pop_front();
if(Q[best].empty()) {if(best == 0) {sort(num+1,num+n+1); int last = -1; ans = 0; for(int i = 1; i <= n; ++i) if(num[i] != last){++ans; last = num[i];} printf("%d", ans); return ;} --best; continue;}
int now = Q[best].front();
Q[best].pop_front(); v[now] = true;
for(int i = head[now]; i; i = edge[i].next) {
int y = edge[i].to;
if(v[y]) continue;
num[y]++;
best = max(best, num[y]);
Q[num[y]].push_front(y);
}
ans = num[now];
}
return ;
}
int main() {
freopen("1006.in", "r", stdin);
n = read(); m = read();
memset(head, 0, sizeof(head)); cnt = 0;
for(int i = 1; i <= m; ++i) {
int x = read(), y = read();
add(x, y); add(y, x);
}
solve();
return 0;
}