题意
给定一个 n n n 个点, m m m 条边的无向图,现要给图中每一个点赋予一个温度值,使其满足下述三个条件:
- 每个点的温度在 [ a , b ] [a,b] [a,b] 范围内
- [ a , b ] [a,b] [a,b] 范围内的所有整数在原图中都至少有一个点对应
- 任何两个直接相连的点温差等于 1 1 1
( 1 ≤ n ≤ 2000 , 0 ≤ m ≤ 50000 , 0 ≤ a ≤ b ≤ n ) (1\leq n\leq 2000, 0\leq m\leq 50000,0\leq a\leq b\leq n) (1≤n≤2000,0≤m≤50000,0≤a≤b≤n)
题目链接: l i n k link link
思路
比赛时看了题意之后没有直接的思路,于是选择莽另外一题,然后 w a wa wa 14 14 14 发…自闭结束比赛。
赛后知道做法之后,感觉分析链还是较为直接的,现将分析过程表达如下。
首先如果本图是一个链,那就从起点依次向下染色,相邻点不同色即可。于是问题在于环应该如何处理。
首先对环的种类进行枚举,如果环是奇环,即环上边的个数为奇数,那么很明显,我们是没有办法对奇环进行温度赋值使其满足条件的,因此如果有奇环则一定不合法,即我们只需要考虑偶环的情况。
图中只有偶环,则是一个典型的二分图,即一定存在一种涂色方案使得该图中直接相连的点温差等于 1 1 1,这里需要特判一下 a = b a=b a=b 的情况。
回顾一下目前的局势,我们将问题转换为了:
- 有奇环则一定不可行
- 无奇环则一定可以涂色
于是接下来我们需要考虑「图中最多的不同涂色个数由什么决定?」显然这取决于图中距离最远的两个点,其距离决定了不同颜色个数。
由于 n n n 的大小只有 2000 2000 2000,因此我们直接 O ( n ) O(n) O(n) 枚举起点,再 O ( n ) O(n) O(n) 从起点出发 b f s bfs bfs 遍历整个图,于是拉起了一颗 b f s bfs bfs 生成树。
从第一层出发,对每一层的节点依次染色,每一层的节点均同色,染色方案为 a → b → a a\rightarrow b\rightarrow a a→b→a 循环往复(因为 b − a + 1 b-a+1 b−a+1 可能远远小于层数)。
这里需要注意,我们可以通过 b f s bfs bfs 生成树直接判断奇环,因为在 b f s bfs bfs 生成树中,第 i i i 层的节点只能到达第 i i i 或 i + 1 i+1 i+1 层的节点。如果第 i i i 层的节点可以到达另一个第 i i i 层的节点,则图中一定存在奇环,即不合法。
最后需要考虑图可能不连通,则我们需要对每一个连通块分别处理,计算最多可表示的颜色个数,再与 b − a + 1 b-a+1 b−a+1 比较即可。
反思
回顾一下这道题,主要有两个点在比赛时没有识别到。
一是图中没有奇环,即为二分图,则一定存在合法染色方案。
二是通过枚举起点拉起一颗 b f s bfs bfs 生成树,计算图中最远点距离,将最多颜色数与 b − a + 1 b-a+1 b−a+1 进行比较。
面对一个复杂问题,最重要需要做的就是分析问题,考虑情况,对整个问题进行抽丝剥茧,核心考察能力就是分析能力。因此在之后的做题过程中需要着重培养这种从细微处不断思考的分析能力,继续加油!
代码
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a; i <= b; i++)
const int N = 3000+10;
using namespace std;
int n,m,a,b,ans[N],d[N],dir[N],pos[N];
vector<int> G[N], nv[N];
queue<int> q;
int bfs(int s, int id = 0) {
int maxn = 1;
while(q.size()) q.pop();
q.push(s); d[s] = 1;
while(q.size()) {
int x = q.front(); q.pop();
if(id) nv[id].push_back(x);
for(auto y:G[x]) {
if(d[y] == d[x]) return 0;
if(d[y]) continue;
d[y] = d[x]+1;
maxn = max(maxn, d[y]);
q.push(y);
}
}
return maxn;
}
int color(int s, int l) {
int maxn = 1;
while(q.size()) q.pop();
q.push(s); d[s] = 1;
ans[s] = l, dir[s] = 1;
if(l == b) dir[s] = -1;
while(q.size()) {
int x = q.front(); q.pop();
for(auto y:G[x]) {
if(d[y]) continue;
d[y] = d[x]+1;
ans[y] = ans[x]+dir[x];
if(ans[y] == b) dir[y] = -1;
else if(ans[y] == a) dir[y] = 1;
else dir[y] = dir[x];
q.push(y);
maxn = max(maxn, d[y]);
}
}
return maxn;
}
bool solve() {
int tot = 0, maxn = 0;
rep(i,1,n) d[i] = 0, nv[i].clear();
rep(i,1,n) {
if(!d[i]) bfs(i, ++tot);
}
rep(i,1,tot) {
int v = 0, hp = 0;
for(auto x:nv[i]) {
for(auto y:nv[i]) d[y] = 0;
int vv = bfs(x);
if(vv == 0 || (a == b && vv > 1)) continue;
if(vv > v) v = vv, hp = x;
}
maxn += v, pos[i] = hp;
}
if(maxn >= b-a+1) {
int l = a;
rep(j,1,n) d[j] = 0;
rep(i,1,tot) {
int v = color(pos[i], l);
l = min(b, l+v);
}
return 1;
}
return false;
}
int main()
{
int _; scanf("%d",&_);
while(_--) {
scanf("%d%d%d%d",&n,&m,&a,&b);
rep(i,1,n) G[i].clear();
rep(i,1,m) {
int u,v; scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
if(solve()) {
printf("Yes\n");
rep(i,1,n) printf("%d%c", ans[i], " \n"[i==n]);
}
else printf("No\n");
}
return 0;
}