这是一道求哈密尔顿回路的问题。
首先介绍一下什么是哈密尔顿回路。哈密尔顿回路是指从一个点出发,经过图中每个点一次且仅一次,最后回到出发点的一条回路。目前没有判断哈密尔顿回路的充要条件,但有一些有用充分条件或必要条件。其中此题要用到其中一条充分条件:如果图G的每个点的度之和大于等于n(n为图G的顶点数),则图G存在哈密尔顿回路。
然后介绍一下算法:
(摘自欧拉回路及哈密顿回路浅见 - 我叫小菜的专栏 - CSDN.NET ,略有改动)
(1)选取任意一个结点S和一个与其相邻结点T。
(2)然后从S和T分别出发,加入与其相关联的节点,逐步向两边扩展至无法扩展为止。
(3)若此时S与T连通,则判断是否S->T中包含全部点,若不连通则查找S->T中结点v[i]与T相邻且v[i+1]与S相邻的点,将V[i+1]到T部分翻转,可构成S->v[i]->T->v[i+1]的哈密顿回路,
(4)判断此时是否路径包含全部结点,若不包含,查找不属于回路中但与其中结点相邻的结点,然后可将此回路缩为一点(因为已经求得当前回路为哈密顿回路)再修改S、T重复上述过程。
更详细的讲解可以参考《ACM—ICPC程序设计系列 图论及应用》(哈尔滨工业大学出版社)P24。
最后 ,简单说一下做这道题的体会。从这道题可以发现用cin,cout输入输出不如用scanf,printf输入输出快。一直以来,我都想保持C++的风格,不去用C中的输入输出函数,可是在这道题上,如果用cin,cout,那么很可能是TLE(我尝试提交过5次,只有一次985MS侥幸通过,其余都是TLE),而改用scanf,printf的话,虽然与那些几十毫秒过的大牛不可相提并论,但能在两三百毫秒内通过,相对于题目所规定的的时间还是可以的。所以今后还是改用scanf,printf,免得麻烦。
顺便提一下,本题还是special judge,所以答案不为一,只要符合题意就行,不必局限于题目的输出答案。
代码(C++):
/*
根据题意和图存在哈密尔顿回路的充分条件,本题一定存在哈密尔顿回路,所以无需考虑不存在答案的情况
*/
#include <cstdlib>
#include <iostream>
#define MAX 4005
using namespace std;
int n,path[MAX];
bool vis[MAX],map[MAX][MAX];
void reverse(int array[],int a,int b)
{
int tmp;
while(a<b)
{
tmp=array[a];
array[a]=array[b];
array[b]=tmp;
a++;
b--;
}
}
void extend(int &amount)
{
int i,u;
u=path[amount-1];
for(i=1;i<=2*n;i++)
{
if(!vis[i]&&map[u][i]) //本来需要判断i!=u,但是vis[i]顺便解决了这个问题
{
path[amount++]=i;
vis[i]=true;
u=i;
i=0;
}
}
}
void hamilton(int amount) //amount记录哈密尔顿回路中的节点数
{
int u,v,i,j;
extend(amount);
reverse(path,0,amount-1);
extend(amount);
u=path[0];
v=path[amount-1];
if(false==map[u][v]) //如果不能形成一个回路,就构造一个回路
{
for(i=1;i<amount-1;i++)
{
if(map[path[i]][v]&&map[u][path[i+1]]) break;
}
reverse(path,i+1,amount-1);
}
if(amount!=2*n)
{
for(i=1;i<amount-1;i++)
{
u=path[i];
for(j=1;j<=2*n;j++)
{
if(map[u][j]&&!vis[j]) break;
}
if(j<=2*n) break;
}
reverse(path,0,i-1);
reverse(path,i,amount-1);
hamilton(amount);
}
}
int main(int argc, char *argv[])
{
int m,i,j,u,v;
while(scanf("%d %d",&n,&m)&&n+m!=0)//cin>>n>>m
{
memset(map,true,sizeof(map));
for(i=0;i<m;i++)
{
scanf("%d %d",&u,&v);//cin>>u>>v;
map[u][v]=map[v][u]=false;
}
memset(vis,false,sizeof(vis));
for(i=1;i<=2*n;i++)
{
for(j=1;j<=2*n;j++)
{
if(i!=j&&map[i][j]) break;
}
if(j<=2*n) break;
}
path[0]=i;
path[1]=j;
vis[i]=vis[j]=true;
hamilton(2);
printf("%d",path[0]);//cout<<path[0];
for(i=1;i<2*n;i++) printf(" %d",path[i]);//cout<<' '<<path[i];
printf("\n");//cout<<endl;
}
system("PAUSE");
return EXIT_SUCCESS;
}
题目:
Time Limit: 1000MS | Memory Limit: 65536K | |||
Special Judge |
Description
Now we assume that there are 2 * n children who sit around a big table, and that none has more than n - 1 "enemies".
Input
There will be a blank line between input blocks. And m = n = 0 indicates the end of input and this case shouldn't be processed.
Output
Sample Input
1 0 2 2 1 2 3 4 3 6 1 2 1 3 2 4 3 5 4 6 5 6 4 12 1 2 1 3 1 4 2 5 2 6 3 7 3 8 4 8 4 7 5 6 5 7 6 8 0 0
Sample Output
1 2 4 2 3 1 1 6 3 2 5 4 1 6 7 2 3 4 5 8