Genghis Khan the Conqueror----HDU_4126----最小生成树_DFS_DP

题目地址:  题目地址,打开自动链接

这个题目的意思很明确,就是给了你一张图,然后有Q次询问,每次会将一条边的权值变大,注意:是变大哦亲。然后要你求出每改变一次之后的最小生成树的权值,最后权值之和除以询问的次数。首先有一个很简单的想法,那就是每询问一次就求一次最小生成树,最后相加。但是这无疑太耗时间,TLE绝对会在那等着你。那么有木有别的方法可以解决这个问题呢?在经历了近一天的查询和思索之后,总算把这个题A了。

根据某位大牛的博客而来的。

首先是进行一次prime的最小生成树算法,在这个算法的过程中记录下每次加进来的点的路径,也就是说这个点是由哪个加进来的。以及每次加进来的边长,这是为了计算生成树的权值用的,分别是pre和total这两个数组。然后从生成树的0号开始DFS算法,给生成树上的每个点进行一次编号。目的就是为了在计算生成树上点i到其他子树距离的时候用的。这里用到了两个数组,一个是点x是第几个,用到是dfn,另外一个就是ndfn意思是第i个是x。然后就是通过work函数来求出点i到其余子树的最近距离。这里就用到了DP的算法。针对第j个在生成树上的点,i到它的前驱所在树的距离是i与pre[j]的距离以及i到j所在树的距离的较小值,

状态转移方程为:mins[i][ dfn[ pre[ ndfn[j] ] ] ] = min(mins[i][j], mins[i][dfn[pre[ndfn[j]]]]);

这些都做完之后,接下来要处理的就是询问的问题了。首先把还没改动的最小生成树的值加上,然后后面的每次值针对改动的加就可以了。首先如果x,y两点之间的边根本就不在生成树中那么也就每必要进行修改(我的代码里面说的很清楚)。如果在,那么我们就要保证x是y的父亲节点,也就是x在生成树中的序要比y小。如果不是,进行交换。然后就先求出y到x所在树上的最小值,然后再求x到y的所在树上的最小值,取最小的那一个,那样求出来的就是x所在树和y所在树的之间的最短距离了。但是要注意的是这里x和y之间的原来那条边并没有拿进来比较,因为这条边正在被修改,所以是不在考虑范围内的。刚刚求出来的那个最小值在和修改的值比较,取两者较小的那一个然后再减去原来那条边的值除以Q加到我们的SUM中去。这样经历Q次循环,我们就求出了最终结果了,下面附上完美注释版:

#include
#include
#include
#include
using namespace std;

#define MAXN 3005
#define INF 999999999
int n,m,mark;
int map[MAXN][MAXN];
int pre[MAXN];
int total[MAXN];
int dfn[MAXN];
int ndfn[MAXN];
int sons[MAXN];
int mins[MAXN][MAXN];
int ans[MAXN];

bool set[MAXN];

void init()
{
	memset(map,-1,sizeof(map));
	for(int i=0;i= 0 && !set[i])
			{
				if(total[i] < 0 || map[x][i] < total[i])
				{
					pre[i] = x;
					total[i] = map[x][i];
				}
			}
		}

		//下面的这个过程就是找最近的过程
		//不懂的话就好好的体会一下
		f = false;
		for(int i=0;i=0)
			{
				if(!f || total[i] < total[x])
				{
					x = i;
					f= true;
				}
			}
		}
	}
}


void dfs(int x)
{
	sons[x] = 1;
	/*
	 * dfn表示的是这个点在这个树中是第几个节点
	 * ndfn表示的是第几个节点是x
	 * 这些都是针对已经在最小树上来说的
	 */
	dfn[x] = mark;
	ndfn[mark++] = x;

	for(int i=0;i=0 ? map[i][j]:INF;
		}
	}

	for(int i=0;i0;j--)
		{
			mins[i][ dfn[ pre[ ndfn[j] ] ] ] = min(mins[i][j], mins[i][dfn[pre[ndfn[j]]]]);
		}
	}
}

void query()
{
	memset(ans,-1,sizeof(ans));
	int q;
	double sum = 0;
	for(int i=0;i dfn[y] + sons[y] - 1))
				{
					ans[y] = min(ans[y],mins[dfn[i]][dfn[y]]);
				}
			}
			//下面就是要找出x到y所在子树上的最小距离
			for(int i=1;i





你可能感兴趣的:(综合性题目,搜索)