Codeforces Gym 100431A Achromatic Number 欧拉回路
原题链接:http://codeforces.com/gym/100431/attachments/download/2421/20092010-winter-petrozavodsk-camp-andrew-stankevich-contest-37-asc-37-en.pdf
题意
给你一个n,让你构造一个环,使得相邻的节点的颜色不一样,问最多能使用多少颜色,并且输出染色方案。
题解
首先,我们来考察对于k个颜色,我们至少需要多少节点。每个点可以连接两个点,将边考虑为有向边,如果我们不浪费任何一条边,对于一个颜色,我们将它连到其余的一半的颜色,将另外一半的颜色连接到他,由于可能除不尽,所以要向上取整。这样,我们得到了一个简单的公式,对于偶数的颜色k,我们需要k*k/2的点,对于奇数的颜色k,我们需要k*(k-1)/2的点。
现在反过来思考,对于给定的节点数,我们至少需要多少颜色。如果刚刚够用,自然最好,如若不然,就寻找一个合适的k,然后采取在多于的节点随便染色的策略。但事实并非如此,因为题意要求是个环,那么我们如若在最后补颜色,那么就会拆开最后一条边,所以,如果给的n只比合适值多1,那么颜色数就要减一,这就是为什么4个点的答案是2而3个点的答案是3。
接下来,我们来考虑如何构造解。对于我们已经构建好的有向图,本质上是要遍历所有的边。那么只需要在最初弄好的颜色的有向图上跑一发欧拉回路。然后我们需要填充多于的节点,为了避免出现拆开唯一的颜色边,所以应当将一个重复的边拆开,再在里面插点。插点时需要满足题意的条件。
此题坑非常多,需要十分细致,最好手算一下前10的答案,然后比对一下输出。
代码
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<vector> #define MAX_N 1234 using namespace std; int gao[MAX_N]; int k; struct edge { bool vis; int to; edge(int t) :vis(0),to(t){} edge(){} }; vector<edge> G[MAX_N]; int tot=0,road[MAX_N]; bool flag=false; int n; int st[MAX_N],all=0; void dfs(int u) { //cout<<u<<endl; int i = gao[u]; while (i < G[u].size() && G[u][i].vis)i++; if (i == G[u].size())return; gao[u] = i + 1; G[u][i].vis = 1; st[all++] = G[u][i].to; dfs(G[u][i].to); } bool check(int u) { //cout<<G[u][0].vis<<endl; if (gao[u] == G[u].size())return false; return true; } void Fluery(int s) { st[all++] = s; while (all) { int now = st[all - 1]; if (check(now))dfs(now); else { //cout<<rHash(now)<<endl; road[tot++] = now; all--; } } } bool vis[MAX_N][MAX_N]; int newRoad[MAX_N]; int main() { freopen("achromatic.in","r",stdin); freopen("achromatic.out","w",stdout); scanf("%d", &n); for (k = 0; ; k++) { int t; if (k & 1)t = k * (k - 1) / 2; else t = k * k / 2; if (t > n)break; } k--; if ((k & 1) && n == (k * (k - 1)) / 2 + 1)k--; printf("%d\n", k); for (int i = 0; i < k; i++) for (int j = 1; j <= k / 2; j++) { int u = i; int v = (i + j) % k; G[u].push_back(edge(v)); } Fluery(0); int pos = 0; for (int i = 0; i < tot - 1; i++) { int u = i, v = (i + 1) % (tot - 1); if (vis[u][v]) { pos = u; break; } vis[u][v] = vis[v][u] = 1; } int nt = 0; for (int i = pos + 1; i < tot - 1; i++) newRoad[nt++] = road[i]; for (int i = 0; i <= pos; i++)newRoad[nt++] = road[i]; for (int i = 0; i < tot - 1; i++) printf("%d ", newRoad[i] + 1); if (n == tot - 1) { return 0; } if (n - (tot - 1) == 1) { int j = 1; while (j == newRoad[tot - 2] + 1 || j == newRoad[0] + 1) { j++; if (j == k + 1)j = 1; } printf("%d\n", j); return 0; } printf("%d ", newRoad[0] + 1); int p = newRoad[0] + 1; for (int i = tot; i < n; i++) { int j = 1; while (j == p || (i == n - 1 && j == newRoad[0] + 1)) { j++; if (j == k + 1)j = 1; } printf("%d ", j); p = j; } printf("\n"); return 0; }