Time Limit: 1 second
Memory Limit: 128 MB
【问题描述】
在一片茂密的原始森林中,生物学家们发现了几种远古时期的动物化石。他们将化石依次编号为1,2,3,……n-1,n,然后带回实验室。
经过简单的分析,他们发现了一些规律:若动物A是由动物B经过若干次进化而成的,则称B是A的祖先,特别地,任意一种动物都
是它自己的祖先(经过0次进化);若动物A是由动物B经过一次进化而成的,则称B是A的直系祖先;1号动物无直系祖先,其它每
种动物都有且仅有一个直系祖先;发现的动物化石中,1号的是它们共同的祖先;所有的动物都是从低级向高级进化。
为了进一步确定生物进化的过程,生物学家们经过复杂的分析和计算,得出这些化石两两间的“差异程度”。“差异程度”有如
下性质:对于两个物种i,j,它们的“差异程度”是一个非负整数,记为Dij;Dii=0,i<>j时,Dij=Dji<>0;若i是k的祖先,k
是j的祖先则Dij=Dik+Dkj;若k是i和j的所有公共祖先中最高级的,则Dij=Dik+Dkj。因此,利用“差异程度”,就可以确定生
物进化的过程。
但是化石数量实在太多了,于是,生物学家们希望你能够帮助他们。
【输入格式】
输入文件evolve.in。第一行是一个整数n(1<=n<=100),表示化石种类数。接下来n行,每行n个整数,第i行第j个是Dij(0<=Dij<=100000),表示i和j的“差异程度”。
【输出格式】
输出文件evolve.out。n-1行,第i行是一个整数c,表示i+1号动物的直系祖先为c。
【数据规模】
Sample Input1
4
0 1 4 7
1 0 5 8
4 5 0 3
7 8 3 0
Sample Output1
1
1
3
【题目链接】:http://noi.qz5z.com/viewtask.asp?ID=u247
【题解】
这是个普利姆算法的模板题,但不好想;
题目中提到了1号节点是所有节点的祖先->那么这个1号节点肯定是最后形成的树的根节点(为什么是树?因为有说每个节点只有一个直系祖先!,最后形成的直系祖先树肯定只有n-1条边);
在做普利姆算法的时候,我们会先加入第一个节点(1号);然后查看与1号节点相连的点;
扩展当前形成的最小生成树内所有的节点(一开始只有1号节点)与那些能够通过这个生成树里面的某个点通过一条边直接到达的点最小的距离;放在dis里面;
则我们在用普利姆做扩展的时候;
会把其他节点加入到这个生成树中->为什么是选择离该节点最近的边?因为有说i->k->j(箭头表示x是y的父亲)有dis[i][j] = dis[i][k]+dis[k][j];则两个直系关系dis[i][j]肯定是i的出边里面边权最小的(尽量最小);
在加入的过程中可以保证那个新加入的节点是1号节点的子代节点(保证1号节点是所有节点的祖先);
然后这个点到生成树的某个点的距离是其他不在生成树里面的点距离生成树的某个点的距离中最短的;
这就保证了我们要连的边是一个直系的关系;再用这个新加入的点更新整个生成树到其他不在生成树里面的点的距离就好(尝试);
好恶心啊;蒟蒻都不会。
【完整代码】
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <string>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
typedef pair<int,int> pii;
typedef pair<LL,LL> pll;
void rel(LL &r)
{
r = 0;
char t = getchar();
while (!isdigit(t) && t!='-') t = getchar();
LL sign = 1;
if (t == '-')sign = -1;
while (!isdigit(t)) t = getchar();
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
r = r*sign;
}
void rei(int &r)
{
r = 0;
char t = getchar();
while (!isdigit(t)&&t!='-') t = getchar();
int sign = 1;
if (t == '-')sign = -1;
while (!isdigit(t)) t = getchar();
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
r = r*sign;
}
const int INF = 0x3f3f3f3f;
const int MAXN = 110;
const int dx[5] = {0,1,-1,0,0};
const int dy[5] = {0,0,0,-1,1};
const double pi = acos(-1.0);
int dis[MAXN],n;
int w[MAXN][MAXN],pre[MAXN];
bool in[MAXN];
int main()
{
//freopen("F:\rush.txt","r",stdin);
rei(n);
rep1(i,1,n)
rep1(j,1,n)
rei(w[i][j]);
memset(in,false,sizeof(in));
memset(dis,INF,sizeof(dis));
dis[1] = 0;
rep1(i,1,n)
{
int k = 0,mi = INF;
rep1(j,1,n)
if (!in[j] && dis[j]<mi)
{
k = j;mi = dis[j];
}
if (k==0)
break;
in[k] = true;
rep1(j,1,n)
if (!in[j] && dis[j] > w[k][j])
{
dis[j] = w[k][j];
pre[j] = k;
}
}
rep1(i,2,n)
cout << pre[i]<<endl;
return 0;
}