您的位置:首页 > 其它

清华OJ TSP旅行商问题

2015-11-24 17:49 731 查看

一、题目

1、描述

Shrek是一个大山里的邮递员,每天负责给所在地区的n个村庄派发信件。但杯具的是,由于道路狭窄,年久失修,村庄间的道路都只能单向通过,甚至有些村庄无法从任意一个村庄到达。这样我们只能希望尽可能多的村庄可以收到投递的信件。

Shrek希望知道如何选定一个村庄A作为起点(我们将他空投到该村庄),依次经过尽可能多的村庄,路途中的每个村庄都经过仅一次,最终到达终点村庄B,完成整个送信过程。这个任务交给你来完成。

2、输入

第一行包括两个整数n,m,分别表示村庄的个数以及可以通行的道路的数目。

以下共m行,每行用两个整数v1和v2表示一条道路,两个整数分别为道路连接的村庄号,道路的方向为从v1至v2,n个村庄编号为[1, n]。

3、输出

输出一个数字,表示符合条件的最长道路经过的村庄数。

4、限制

1 ≤ n ≤ 1,000,000

0 ≤ m ≤ 1,000,000

输入保证道路之间没有形成环

时间:2 sec

空间:256 MB

5、例子

Input

4 3

1 4

2 4

4 3

Output

3

二、思考

这是一个NP问题,典型方法可以采用回溯法,简而言之就是在解空间中进行搜索。解空间是一棵排列树,粗略想来应该有O(n!)复杂度。回溯法的本质就是图的深度优先搜索。

由于题目中给出这是一个有向无环图模型(DAG),采用本质为遍历的方法.对于DAG的遍历,可以采用图的拓扑排序算法,其复杂度为O(n+e), e为边的总数,这约等于O(n!).可以证明,任何一个DAG,都至少存在一种拓扑排序。

拓扑排序:是将DAG的顶点排成一个线性序列,其次序必须与原图相容,即每一顶点都不会通过边指向前驱顶点。算法步骤简而言之:顺序输出零入度顶点

//将所有入度为零的顶点存入栈S
while(!S.empty()){
v = S.pop(); // 取栈顶元素
for each edge(v, u) // v的邻接顶点u若入度仅为1
if(u.inDegree < 2)
S.push(u); // 则入栈
G = G \ {v}; // 删除v及其关联边 (邻接顶点入度减为1)
}


可以在拓扑排序的过程中,对于每一个顶点,更新某条路径经过该顶点时,其对应的村庄数,从而得到最长道路经过的村庄数:即最大的村庄数。

给出AC代码:

由于最大内存不超过256MB,如果构建图采用邻接矩阵会超过该数值,因而采用邻接表,记录每个顶点的邻接顶点. 代码可以继续优化。

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE  1000001

typedef struct VertexNode
{
int num;
int inDegree;
int outDegree; //出度 == 邻接点个数
int neiboIndex; //邻接点索引
VertexNode* neibo;
VertexNode()
{
num = 0;
inDegree = 0;
outDegree = 0;
neiboIndex = 0;
neibo = NULL;
}
~VertexNode()
{
if (outDegree)
{
//delete[] neibo;
free(neibo);
}

}
}*pNode;

VertexNode v[MAXSIZE];

void createGraph(const int& n, const int& m)
{
int* edge_start = new int[m];
int* edge_end = new int[m];
for (int i = 0; i < m; ++i)
{
scanf("%d %d", &edge_start[i], &edge_end[i]);
int start = edge_start[i];
int end = edge_end[i];
if (!v[start].num)
{
v[start].num = start;
}

if (!v[end].num)
{
v[end].num = end;
}

v[start].outDegree++;
v[end].inDegree++;
}

//建立邻接点
for (int i = 1; i <= n; ++i)
{
int neiboNum = v[i].outDegree;
if (neiboNum)
{
//v[i].neibo = new VertexNode[neiboNum];
v[i].neibo = pNode(malloc(neiboNum * sizeof(VertexNode)));
}

}

for (int i = 0; i < m; ++i)
{
int start = edge_start[i];
int end = edge_end[i];

pNode p = v[start].neibo;
p[v[start].neiboIndex] = v[end];
v[start].neiboIndex++;

}

delete[] edge_start;
delete[] edge_end;
}

class Stack
{
public:
Stack(int n)
{
top = 0;
data = new int
;
}
~Stack()
{
delete []data;
}
void push(const int& value)
{
data[top++] = value;
}
int& pop()
{
return data[--top];
}
bool isEmpty()
{
return top == 0;
}

protected:
private:
int* data;
int top;
};

int topu(Stack& s, const int& n)
{
int maxVillages = 1; // 最大村庄数
int* villages = new int[n+1];//记录每个顶点对应的村庄数
for (int i = 1; i <= n; ++i)
{
villages[i] = 1;
}

// 对于入度为0的顶点入栈
for (int i = 1; i <= n; ++i)
{
if ( v[i].inDegree == 0)
{
s.push(i);
}
}

while(!s.isEmpty())
{
int nodenum = s.pop(); //取栈顶元素

for (int i = 0; i < v[nodenum].outDegree; i++)
{
pNode p = &((v[nodenum].neibo)[i]);

if (p)
{
if ( villages[nodenum] + 1 > villages[p->num])
{
villages[p->num] = villages[nodenum] + 1;
}

if (maxVillages < villages[p->num])
{
maxVillages = villages[p->num];
}

if ( --v[p->num].inDegree == 0) // 此处不能写成p->inDegree, 因为邻接点是new出来的,和原始地址不一样
{
s.push(p->num);
}
}

}

}

return maxVillages;
}

int main()
{

int n,m;
scanf("%d %d", &n, &m);

createGraph(n, m);

Stack s(n);
int maxVillages = topu(s, n);

printf("%d\n", maxVillages);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: