2018 7.13图论测试总结(Day 10)

  今天,考试。题目如下:

NO1:一件机房里有 n 台电脑,并且有 n-1 个虚拟路径使得 n 台电脑联通,每条虚 拟路径的长度都为 1。老师希望所有电脑到中心 电脑虚拟路径的距离之和最小,求中心电脑编号和距离总和最小值。

对于 60%的数据,1≤n≤1000。 对于 100%的数据, 1≤n≤50000。

这题没过,60,也就是60%的数据,暴力思想:枚举每一给点,求其到每一个点的最短路径,相加在比较。顺便记录一下位置。和香甜的黄油差不多,时间复杂度......(不想算,反正超N^的)至于代码。会写SPFA的应该都会写。

满分思想:其实我们可以发现,我们要找的点就是 树的中重心,至于为什么,我也不知道,所以就先去求树的重心。代码……

int minn=100000;
int f[100001],vis[100001];
void dfs(int fa,int u)
{
	vis[u]=1;
	for(int i=Lin[u];i;i=e[i].x)
	{
		if(e[i].y!=fa)
		{
	    int x=e[i].y;
	    dfs(u,x);
	    vis[u]+=vis[x];
	    f[u]=max(f[u],vis[x]);
	    }
	}
	f[u]=max(n-vis[u],f[u]);minn=min(f[u],minn);return ;
}

还是满清楚的(如果连重心都不会,没救了……)求好了树的重心,就可以用SPFA了

queue<int>q;
int d[50001];
bool v[50001];
void spfa(int k)
{
	memset(d,10,sizeof(d));
	memset(v,0,sizeof(v)); 
	q.push(k);
	d[k]=0;v[k]=1;
	while(!q.empty()) 
	{
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=Lin[x];i;i=e[i].x)
		{
			int y=e[i].y,z=e[i].v;
			if(d[y]>d[x]+z){d[y]=d[x]+z;if(!v[y])q.push(y),v[y]=1;}
		}
	}
	return ;
}

然后,这道题就神不知,鬼不觉的A了……大笑

至于完整带码嘛……(还是你们自己打吧)

NO2:一间机房里有 n 台电脑,需要重新修建 n-1 条虚拟路径使得 n 台电脑联通。 现在给出若干个电脑对,它们之间可以修建虚拟路径并且都有相应的价格。求一种虚拟路 径中最高价格与最低价格差值最小的方案。

对于 30%的数据,1≤n≤m≤20。 对于 100%的数据,1≤n≤m≤5000,0≤c≤50000。

这题我最想吐槽,95分!!!!对了19个点,错了一个!!玩毛大哭

30分思想:

小范围爆搜。因为边数非常少,所以我们可以暴力枚举每条边选还 是不选,然后判断是否选出的边能构成生成树,可以的话更新答案取最 小。时间复杂度O(m2 ×n)。

满分思想:很容易就能看出,这是一到穷举的题目,把边权按从小到大排序,然后我们开始枚举生成树中最小的边,右 指针不断右移,用并查集来合并集合,我们只选连接两个不同集合的 边,如果取了n-1条则停下,此时的右指针就是指在这棵下界为左指针 边权的生成树上的最大边,我们更新一下答案。 其实就是在最小边确定的情况下使最大边尽量小,时间复杂 度O(m2 + n×m) ,就是kurskal;

emmmmm.......

struct edge {
	int u,v,w;
	friend bool operator<(edge a,edge b) {return a.w<b.w;}
}e[10001];
int f[10001];
int getf(int x)
{
	if(f[x]==x)return x;
	  else return f[x]=getf(f[x]);
}
int prim()
{
	int ans=-1;
	sort(e+1,e+m+1);
	for(int i=m;i>=n-1;i--)
	{
		for(int j=1;j<=n;j++)
		  f[j]=j;
		int sum=0;
		for(int j=i;j>=1;j--)
		{
			int v=getf(e[j].v),u=getf(e[j].u);
			if(v!=u){sum++;f[v]=u;}
			if(sum==n-1)
			{
				if(e[i].w-e[j].w<ans||ans==-1)ans=e[i].w-e[j].w;
				break;
			}
		}
	}
    return ans;
}

这也是整个代码的核心,函数不是用的Prim算法,而是用kurskal,懒,不想写……(不要学我)

大笑

NO3:在一个n×m 初始值为0 的矩阵中,每次可使某一行或某一列加一 或减一。设ai 表示第i 行加的数字和,bj 表示第j 列加的数字和。给出 若干个ai + bj = ci,j的限制,问是否存在满足的操作序列。

对于 20%的数据,1≤n,m≤2。 对于 50%的数据 1≤n,m≤50。 对于 100%的数据 1≤n,m≤1000

对于20%的数据: 首先,如果有一个格子没有限制,那就一定能构造出解。考虑 有4个限制的情况,相当于给四个四元一次方程,求解。手推就能算出 不合法的情况。OK!

对于50%的数据,有1 ≤ n,m ≤ 50。 对所有限制进行高斯消元,时间复杂度是O((n + m)2 ×k)。

我现在还不会,所以略过……

对于100%的数据,有1 ≤ n,m ≤ 1000。 将ai + bj = ci,j 拆成ai + bj ≤ ci,j 和ai + bj ≥ ci,j 两个限制。 即ai −(−bj) ≤ ci,j 和ai −(−bj) ≥ ci,j 。 即ai −(−bj) ≤ ci,j 和(−bj)−ai ≤−ci,j 。 对于每个ai 和−bj 开一个节点,每个限制连一条边,就变成差分约 束判断有没有解的问题了(也就是……差分约束)

核心代码如下:

int d[50001];
bool v[50001];
int num[50001];
bool spfa()
{ 
    queue<int>q;
    memset(d,0,sizeof(d));
    memset(v,0,sizeof(v));
    memset(num,0,sizeof(num));
    for(int i=1;i<=n+m;i++)q.push(i);
	while(!q.empty()) 
	{
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=Lin[x];i;i=e[i].x)
		{
			int y=e[i].y,z=e[i].v;
			if(d[y]>d[x]+z)
			{
			  d[y]=d[x]+z;
			  if(!v[y]){num[y]++;q.push(y);v[y]=1;if(num[y]>n+m)return 1;}
			}
		}
		
	}
	return 0;
}

接下来回陆续更新直到7月22号,前面的我也会一一补齐来(每两天更新一次)。大笑


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值