边着色问题:给图的每条边制定一种颜色,使得任意两条相邻的边的颜色均不相同
为了解决本次的问题首先需要引入:Misra & Gries边着色算法
Misra & Gries边着色算法是图论算法的一种,能够在多项式内找到任意图的一种边着色方案。这种着色算法最多使用 Δ + 1 \Delta+1 Δ+1种颜色, Δ \Delta Δ是该图节点的最大度数。这对于一些图而言是最优的,根据Vizing定理,最坏情况下,这种算法给出的结果比最优值多使用一种颜色
论文: “A constructive proof of Vizing’s theorem”
首先介绍几个概念:
介绍扇之前需要先引入定义.对于一种颜色x,如果 c o l o r ( u , z ) ≠ x color(u,z)\ne x color(u,z)̸=x对所有的 ( u , z ) ∈ E ( G ) ( z ≠ v ) (u,z)\in E(G)(z\ne v) (u,z)∈E(G)(z̸=v)成立,则称这种颜色x对边(u,v)未使用
再来引入扇的定义.
对顶点u的一扇,是一个顶点序列,记为 F [ 1 : K ] F[1:K] F[1:K]该序列满足:
给定一个扇F,任意边 ( F [ i ] , x ) (F[i],x) (F[i],x) ( 1 ≤ i ≤ k ) (1\le i\le k) (1≤i≤k)是扇的一条边.现在令两个颜色c与d,则一个 c d x cd_x cdx路径定义为经过节点x,由只含c,d颜色的边组成的最大路径(即不能再添加更多的边)
对于一个给定的节点x的一个扇F[1:k],旋转即是进行:
由于前面提到的扇子的性质( c o l o r ( u , F [ i + 1 ] ) color(u,F[i+1]) color(u,F[i+1])对于 ( F [ i ] , u ) (F[i],u) (F[i],u)未使用)因此旋转后,其染色依然是满足条件的
前面提到的路径 c d x cd_x cdx,对其进行翻转即是将路径上所有的颜色为c的边的颜色改为颜色d,所有颜色为d的边的颜色改为c.如果x是路径末端的点.则这一操作会将使得点x多释放出一种颜色.因为这一操作对于边上点,翻转操作只是交换了边的颜色,而未增加新的颜色,因此其并不会改变染色的有效性.而对于在端点上的x其原本只有c或者d中的一种,暂且假设为d,在翻转之后其就变成了c,那么d这一颜色就空了出来.就对于x增加了一种颜色
有了扇的定义之后就可以描述这一算法了:
输入:图G
输出:对图G的一个合适的染色方案
令 U : = E ( G ) U:=E(G) U:=E(G)
while |U|!=0 do
令(u,v)为集合U中的任意一条边
令F[1:k]为u的一个最大扇,且F[1]=v
令c为对于u未使用的一种颜色,d为对于F[k]的一种颜色
翻转cdu
令w为扇F中的一个点,令一个新的扇其为F'[F[1]...w]是一个扇,且颜色d对于w未使用
旋转扇F'再将已然无色的(u,w)=d
U:=U-{(u,v)}
end while
有m支队伍,每支队伍有n个选手,让所有的选手和其他组的选手进行一次较量.问怎么安排可以让所有的选手在一轮中只出现一次.且最多一轮不出现.
可以将这个问题转换成上面介绍的边染色问题.将两个选手在哪一出战转化成选手之间连边建图,对图进行染色并得到结果.由于Misra&Gries边着色算法具有可以得到最少着色的特点(具体为什么这就是最少的着色不再赘述,可以看论文了解),因此,直接由此算法得出染色方案即是答案.
#include
using namespace std;
typedef pair<int,int> pii;
const int sz=26*26;
int match[sz][sz];
bool vis[sz];
bool col[sz];
int n,m;
int get_block(int x)
{
memset(col,0,sizeof(col));
for(int i=0;i<n*m;i++) col[match[x][i]]=1;
for(int i=1;i<n*m;i++) if(!col[i]) return i;
return n*m;
}
int check_conflict(int x,int loc)
{
for(int i=0;i<n*m;i++) if(match[x][i]==loc) return i;
return n*m;
}
void recol(int x, int y)
{
int pre_match=get_block(y);
int conflict=check_conflict(x,pre_match);
memset(vis,0,sizeof(vis));
vis[y] = 1;
vector<pii> match_line;
match_line.push_back(pii(y,pre_match));
while (conflict!=n*m && !vis[conflict]) {
vis[conflict] = 1;
y=conflict;
pre_match = get_block(y);
match_line.push_back(pii(y,pre_match));
conflict = check_conflict(x, pre_match);
}
if (conflict==n*m) {
for(auto t:match_line) {
match[x][t.first] = t.second;
match[t.first][x] = t.second;
}
} else {
int pre_match_x = get_block(x);
int conflict_x=check_conflict(conflict,pre_match_x);
match[x][conflict] = pre_match_x;
match[conflict][x] = pre_match_x;
while (conflict_x!=n*m) {
int temp= check_conflict(conflict_x,pre_match);
match[conflict][conflict_x] = pre_match;
match[conflict_x][conflict] = pre_match;
conflict=conflict_x;
conflict_x=temp;
swap(pre_match_x,pre_match);
}
recol(x,match_line[0].first);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n*m;i++)
{
for(int j=0;j<n*m;j++)
{
if(i/n!=j/n&&!match[i][j])
recol(i,j);
}
}
int tot=n*(m-1)*n*m/2;
for(int k=1;tot;k++)
{
for(int i=0;i<n*m;i++)
{
for(int j=i+1;j<n*m;j++)
{
if(match[i][j]==k)
{
tot--;
printf("%c%d-%c%d ",'A'+i/n,i%n+1,'A'+j/n,j%n+1);
}
}
}
cout<<endl;
}
}