Link:http://poj.org/problem?id=1419
下面解题思路和算法描述来自:http://blog.csdn.net/wall_f/article/details/8190324
思路:在这一题中,题目给定的图一定是二分图,所以我们可以用到一种基于贪心思想的染色算法--顺序染色法,有的图是不能用顺序染色法来求解的。这种算法是一种近似的有效算法。由于每次从u最小的邻接顶点开始染色,所以输出的数字一定是严格的上升序。
顺序染色:
(1)用i表示顶点序号,i=1;
(2)用c表示给顶点i着色为第c中颜色,c=1;
(3)对第i个顶点着色:考虑它的每个邻接顶点,如果都没有使用第c中颜色 ,则给顶点i着色为第c种颜色,并转向第(5)步;否则,转向第(4)步。
(4)c = c+1,并转向第(3)步。
(5)若还有其他的顶点未着色,则i = i+1,并转向第(2)步,否则算法结束。
注意,这只是一种近似的有效算法。例如,二分图中i就可以用顺序染色,而有的图用这种算法求出来的结果是错误的。
My AC code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 100010
#define MOD 1000000007
using namespace std;
const int INF=0x3f3f3f3f;
int color[111];
vectorvec[111];//vec[i]存储第i种着色方案
vectore[111];//边
int cnt,n;//n为顶点数,cnt为黑色的点数 ,cnti可指定求在某一种特定条件下的最优方案是第几种方案
int ans,ansi;
bool Ok(int k)//k表示第k个顶点
{
if(color[k]==1)
{
for(int i=0;iansi)
{
ansi=cnt;
for(int i=1;i<=n;i++)
{
if(color[i]==1)
vec[ansi].push_back(i);
}
}
return;
}
if(cnt+(n-u)+1<=ansi)//剪枝,若就算剩下的点数都可以染成黑色,再加上当前黑色的点数还没有前面的染色方案得到的最大黑色点数多,则舍弃当前这种方案
return;
for(int co=1;co<=color_num;co++)//color_num表示颜色种类数,1表示黑色,2表示白色
{
color[u]=co;//对顶点u染第co种颜色 //第3步或第4步
if(Ok(u)) //第(3)步
{
if(co==1)//当前顶点u染的是黑色,黑色的点数加一
cnt++;
dfs(u+1,color_num);//注意:着色问题深搜是按顶点编号深搜下去,不是沿着邻边的下个顶点深搜下去!!!
color[u]=0;//回溯
if(co==1)
cnt--;
}
}
}
int main()
{
//freopen("D:\in.txt","r",stdin);
int t,i,u,v,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(i=0;i<=n+2;i++)
{
vec[i].clear();
e[i].clear();
color[i]=0;
}
for(i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
cnt=0;
ansi=0;
dfs(1,2);//注意:着色问题深搜是按顶点编号深搜下去,不是沿着邻边的下个顶点深搜下去!!!
printf("%d\n",ansi);
if(ansi!=0)
{
printf("%d",vec[ansi][0]);
for(int i=1;i
Channel Allocation
Description
When a radio station is broadcasting over a very large area, repeaters are used to retransmit the signal so that every receiver has a strong signal. However, the channels used by each repeater must be carefully chosen so that nearby repeaters do not interfere with one another. This condition is satisfied if adjacent repeaters use different channels.
Since the radio frequency spectrum is a precious resource, the number of channels required by a given network of repeaters should be minimised. You have to write a program that reads in a description of a repeater network and determines the minimum number of channels required. Input
The input consists of a number of maps of repeater networks. Each map begins with a line containing the number of repeaters. This is between 1 and 26, and the repeaters are referred to by consecutive upper-case letters of the alphabet starting with A. For example, ten repeaters would have the names A,B,C,...,I and J. A network with zero repeaters indicates the end of input.
Following the number of repeaters is a list of adjacency relationships. Each line has the form: A:BCDH which indicates that the repeaters B, C, D and H are adjacent to the repeater A. The first line describes those adjacent to repeater A, the second those adjacent to B, and so on for all of the repeaters. If a repeater is not adjacent to any other, its line has the form A: The repeaters are listed in alphabetical order. Note that the adjacency is a symmetric relationship; if A is adjacent to B, then B is necessarily adjacent to A. Also, since the repeaters lie in a plane, the graph formed by connecting adjacent repeaters does not have any line segments that cross. Output
For each map (except the final one with no repeaters), print a line containing the minumum number of channels needed so that no adjacent channels interfere. The sample output shows the format of this line. Take care that channels is in the singular form when only one channel is required.
Sample Input 2 A: B: 4 A:BC B:ACD C:ABD D:BC 4 A:BCD B:ACD C:ABD D:ABC 0 Sample Output 1 channel needed. 3 channels needed. 4 channels needed. Source
Southern African 2001
|
AC code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 100010
#define MOD 1000000007
using namespace std;
const int INF=0x3f3f3f3f;
int color[111];
vectorvec[111];//vec[i]存储第i种着色方案
vectore[111];//边
int cnt,n;//n为顶点数,cnt为黑色的点数 ,cnti可指定求在某一种特定条件下的最优方案是第几种方案
int ans,ansi;
bool Ok(int k)//k表示第k个顶点
{
for(int i=0;iansi)
//{
//ansi=cnt;
ansi=color_num;
/*for(int i=1;i<=n;i++)
{
if(color[i]==1)
vec[ansi].push_back(i);
}
}*/
return;
}
//if(cnt+(n-u)+1<=ansi)//剪枝,若就算剩下的点数都可以染成黑色,再加上当前黑色的点数还没有前面的染色方案得到的最大黑色点数多,则舍弃当前这种方案
// return;
for(int co=1;co<=color_num;co++)//color_num表示颜色种类数,1表示黑色,2表示白色
{
color[u]=co;//对顶点u染第co种颜色 //第3步或第4步
if(Ok(u)) //第(3)步
{
dfs(u+1,color_num);//注意:着色问题深搜是按顶点编号深搜下去,不是沿着邻边的下个顶点深搜下去!!!
color[u]=0;//回溯
}
}
}
char s[33];
int main()
{
//freopen("D:\in.txt","r",stdin);
int t,i,j,m;
while(~scanf("%d",&n))
{
if(n==0)
break;
for(j=0;j<=n;j++)
{
vec[j].clear();
e[j].clear();
color[j]=0;
}
for(i=1;i<=n;i++)
{
scanf("%s",s);
int len=strlen(s);
int u=s[0]-'A'+1;
for(j=2;j
回溯经典-m图着色问题(和地图4色问题的区别)
四色问题:
四色问题是m图着色问题的一个特列,根据四色原理,证明平面或球面上的任何地图的所有区域都至多可用四种、颜色来着色,并使任何两个有一段公共边界的相邻区域没有相同的颜色。这个问题可转换成对一平面图的4-着色判定问题(平面图是一个能画于平面上而边无任何交叉的图)。将地图的每个区域变成一个结点,若两个区域相邻,则相应的结点用一条边连接起来。多年来,虽然已证明用5种颜色足以对任一幅地图着色,但是一直找不到一定要求多于4种颜色的地图。直到1976年这个问题才由爱普尔(k.i.apple),黑肯(w.haken)和考西(j.koch)利用电子计算机的帮助得以解决。他们证明了4种颜色足以对任何地图着色。
在这一节,不是只考虑那些由地图产生出来的图,而是所有的图。讨论在至多使用m种颜色的情况下,可对一给定的图着色的所有不同方法。
m图着色问题:
题目大意:
1,已知一个图g和m>0种颜色,在只准使用这m种颜色对g的结点着色的情况下,是否能使图中任何相邻的两个结点都具有不同的颜色呢?这个问题称为m-着色判定问题。
2,在m-着色最优化问题则是求可对图g着色的最小整数m。这个整数称为图g的色数。这是求图的最少着色问题,求出m的值。
题目的解法:
第一个问题,m-着色判定问题:
可以通过回溯的方法,不断的为每一个节点着色,在前面n-1个节点都合法的着色之后,开始对第n个节点进行着色,这时候枚举可用的m个颜色,通过和第n个节点相邻的节点的颜色,来判断这个颜色是否合法,如果找到那么一种颜色使得第n个节点能够着色,那么说明m种颜色的方案是可行的。返回真即可:
//用于判断当前节点上涂上这个颜色可不可行,与其邻接节点的颜色做判断,这里用邻接表来存储图的信息
bool isok(int step)
{
vector::iterator iter;
for(iter = input[step].begin(); iter != input[step].end(); iter++)
{
if(Color[step] == Color[*iter]) return false;
}
return true;
}
//step表示0->n的节点,color_num是指给color_num的颜色的个数可用
//判断如果给color_num的颜色的个数是否可行,如果可行返回true,否则false
bool DFS(int step, int color_num)
{
if(step >= n) return true;
else
{
int i;
for(i = 1; i<= color_num; i++)
{
Color[step] = i;
if(isok(step))
{
if(DFS(step + 1, color_num))
return true;
}
Color[step] = 0;
}
}
return false;
}
第二个问题:求出最少的着色数m
有了上面的问题的积累,对于这个问题就很简单了,只要从1到n枚举颜色数,来调用上面的DFS(0, m),如果有一次调用返回true,那么这时这个颜色就是我们要求的最少的着色数。
for(i = 1; i<= n; i++)
{
if(DFS(0, i))
{
cout << "the min colors :" << i << endl;
break;
}
}
参考:
http://www.cppblog.com/3522021224/archive/2007/06/26/27021.html
http://blog.csdn.net/normalnotebook/archive/2006/06/25/832732.aspx
http://blog.csdn.net/fableboy/archive/2009/03/08/3969452.aspx
下面附上博客:http://blog.csdn.net/songuooo/article/details/7830162 的相关资料,以进一步介绍区间着色问题的求解与应用
顶点着色问题应用
1. n个学生对m个宣讲会中的若干个感兴趣,如何安排宣讲会的时间(每个宣讲会持续的时间相同),使得每个学生对自己感兴趣的宣讲会时间不冲突,且宣讲会的总时间最短?
此问题可以转化成顶点着色问题。
把每个宣讲会看作是一些散布的点,对于每个学生,把他感兴趣的宣讲会之间两两相连,如:
学生A希望参加宣讲会1、2、3
学生B希望参加宣讲会1、3、4 则:
设宣讲会1、2、3、4的着色分别为c1、c2、c3、c2,4与2着同样的颜色,则可以这样安排宣讲会的时间,假设持续时间为一小时,宣讲会1在1点开,2在2点开,3在3点开,4与2时间一样在2点开。
2. 对N个区间进行着色,有重叠的区间不能着同样的颜色。
可转化为顶点着色问题,但顶点着色问题是NP的,至今没有一个已知的好算法,所以对图的色数进行估计以及在寻找使用的颜色数“不太大”的情形下顶点着色的某些方法又有非常重要的意义。于是顶点着色的贪婪算法应运而生。
顶点着色的贪婪算法是按顺序取第一个可用的颜色而忽略对以后的顶点可能会产生的后果。使用正整数对顶点着色,因此可以谈论一种颜色小于另一种颜色了。
(1) 色数
设G =(V,E)是一个图,G的顶点着色就是G的每个顶点指定一种颜色,且使得相邻顶点有不同的颜色。如果这些颜色选自于一个 k 种颜色的集合而不管 k 种颜色是否都用到,那么顶点着色称为 k-顶点着色,简称为k-着色。G有一个 k 着色,那么G是 k-可着色的。使得G是 k-可着色的最小的 k 称为G的色数,用χ(G)表示。
(2) 顶点着色的贪婪算法
设G是一个图,它的顶点按某一顺序记为x1,x2,...,xn。
i ) 对顶点x1指定颜色1.
ii) 对每个 i = 2,3,...,n,另 p 是与 xi 邻接的顶点 x1,x2,...,xi-1 中没有任何一个顶点着色 p 的最小的颜色,并且对 xi 指定颜色 p。
下面是一个定理:
设G是一个图,对于该图顶点的最大度为Δ,那么贪婪算法产生G的顶点的一个-着色(但并不意味着Δ + 1种颜色都用到),因此
(3) 算法设计
回到原始问题:对N个区间进行着色,有重叠的区间不能着同样的颜色。下面是解决这个问题的算法,摘自<编程之美>。
共N个区间,对这N个区间按照区间开始从小到大排序,设为:
[b[i], e[i]] 0 <= i < N
color[i]为颜色号 0 <= i < N
color[i] = 0 i = 0时,
color[i+1] = color[i] 第 i 区间与第 i + 1 区间无重叠时,
color[i + 1] = color[i] + 1 第 i 区间与第 i + 1 区间有重叠时。
算法如下:
int overlap(int b1, int e1, int b2, int e2) { //判断两区间是否有重叠的函数
if (b2 >= e1 || b1 >= e2)
return 0; //没有重叠
else
return 1; //有重叠
}
int intervalColoring(int b[], int e[], int color[], int N)
{
int nMaxColors = 0, i, k, j;
int isForbidden[N];//false:0;true:1
memset(isForbidden, 0, sizeof isForbidden);
for (i = 0; i < N;i++) {
for (k = 0;k < nMaxColors;k++) /** a */
isForbidden[k] = 0;
for (j = 0;j < i;j++) /** b */
if (overlap(b[j], e[j], b[i], e[i]))
isForbidden[color[j]] = 1;
for (k = 0;k < nMaxColors;k++) /** c */
if (isForbidden[color[k]] == 0)
break;
if (k < nMaxColors)
color[i] = k;
else {
color[i] = nMaxColors;
++nMaxColors;
}
}// for
return nMaxColors;
}
(a) 把这当前已着色的nMaxColors种颜色设为可用。
(b) j从区间0开始到区间 i-1与当前待着色区间 i 判断两区间是否有重叠,若有则着区间 j 的颜色不能用来着区间 i 。
(c) k从0到nMaxColors - 1,判断这nMaxColors种颜色有能着区间 i 的。
#include
#include
int overlap(int b1, int e1, int b2, int e2);
int intervalColoring(int b[], int e[], int color[], int N);
int main(void)
{
int i, N = 4;
int b[] = {1, 2, 3, 3}, e[] = {5, 3, 4, 6};
int color[N], nMaxColors = 0;
nMaxColors = intervalColoring(b, e, color, N);
printf("nMaxColors = %d\n", nMaxColors);
printf("\ncolor of the intervals:\n");
for (i = 0;i < N;i++)
printf("interval %d: color %d\n",i, color[i]);
return 0;
}