• 9.19 复习写题报告(生成树)


    9.19 做题报告

    上午主要复习了一下图论中比较基础的最小生成树,打了几道水题(大雾)。

    1.P1195 口袋的天空

    Link

    模板题,只要联通块数变为 (k) 直接 (break) 就行。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,k,fa[10010],ans;
    struct node
    {
    	int u,v,w;
    }e[100010];
    inline int read()
    {
    	int s = 0,w = 1; char ch;
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    bool comp(node a,node b)
    {
    	return a.w < b.w;
    }
    int find(int x)
    {
    	if(fa[x] == x) return x;
    	else return fa[x] = find(fa[x]);
    }
    int main()
    {
    	n = read(); m = read(); k = read();
    	for(int i = 1; i <= m; i++)
    	{
    		e[i].u = read();
    		e[i].v = read();
    		e[i].w = read();
    	}
    	sort(e+1,e+m+1,comp);
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	int num = n;	
    	for(int i = 1; i <= m; i++)//krusal 板子题
    	{
    		int x = e[i].u;
    		int y = e[i].v;
    		int fx = find(x), fy = find(y);
    		if(fx == fy) continue;
    		fa[fx] = fy;
    		ans += e[i].w;
    		if(--num == k) break;
    	}
    	if(num > k) printf("No Answer
    ");
    	else printf("%d
    ",ans);
    	return 0;
    }
    
    2.P1991 无线通讯网

    Link

    也算是一道比较裸的题。

    我们其实求的是当联通块数为 (s) 的时候,所加进去的边权的最大值。

    因为,我们可以给这 (s) 个联通块每个都发一个卫星电话,然后联通块之间的点可以用电话线连接。

    剩下的就是最小生成树的板子啦。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int n,k,tot,fa[1010],x[1010],y[1010];
    double ans;
    inline int read()
    {
    	int s = 0,w = 1; char ch;
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    struct node
    {
    	int u,v;
    	double w;
    }e[510*510];
    int find(int x)
    {
    	if(fa[x] == x) return x;
    	else return fa[x] = find(fa[x]);
    }
    double dis(int i,int j)
    {
    	return sqrt((x[i]-x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
    }
    bool comp(node a,node b)
    {
    	return a.w < b.w;
    }
    int main()
    {
    	k = read(); n = read();
    	for(int i = 1; i <= n; i++)
    	{
    		x[i] = read();
    		y[i] = read();
    	}
    	for(int i = 1; i <= n; i++)
    	{
    		for(int j = 1; j <= n; j++)
    		{
    			e[++tot].u = i;
    			e[tot].v = j;
    			e[tot].w = dis(i,j);
    		}
    	}
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	sort(e+1,e+tot+1,comp);
    	int num = n;
    	for(int i = 1; i <= tot; i++)//krusal 板子
    	{
    		int fx = find(e[i].u); 
    		int fy = find(e[i].v);
    		if(fx == fy) continue;
    		fa[fx] = fy;
    		if(--num == k)
    		{
    			ans = e[i].w;
    			break;
    		}
    	}
    	printf("%.2lf",ans);
    	return 0;
    }
    
    3.P1265 公路修建

    Link

    这题属实把我绕蒙了,绕来绕去就是求个最小生成树的板子题。

    然后自信的打了个 (krusal) 的板子结果 $TLE $, 又 (RE) ,看了看题解才明白,原来这题是不能用 (krusal) 的,因为边的数量太多,

    我们数组存不下,那么只能老老实实的用 (prime) 了。

    一个小优化就是: 我们不必先把所有的距离都求出来,那样数组开不下,我们只需要在用到他的时候再算就可以了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const double inf = 2147483647.0;
    int n,x[5010],y[5010];
    double dis[5010],ans;
    bool vis[5010];
    inline int read()
    {
    	int s = 0,w = 1; char ch;
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    double d(int i,int j)
    {
    	return (double) sqrt((double) (x[i]-x[j]) * (x[i] - x[j]) + (double) (y[i] - y[j]) * (y[i] - y[j]));//一定要转成double 类型的
    }
    int main()
    {
    	n = read();
    	for(int i = 1; i <= n; i++)
    	{
    		x[i] = read();
    		y[i] = read();
    	}	
    	for(int i = 1; i <= n; i++) dis[i] = inf;
    	dis[1] = 0;
    	for(int i = 1; i <= n-1; i++)
    	{
    		int x = 0;
    		for(int j = 1; j <= n; j++)//找当前距离最小的点
    		{
    			if(!vis[j] && (x == 0 || dis[x] >= dis[j])) x = j;
    		}
    		vis[x] = 1;
    		for(int j = 1; j <= n; j++) //更新一下其他点的距离
    		{
    			if(!vis[j])dis[j] = min(dis[j], d(x,j));
    		}
    	}
    	for(int i = 1; i <= n; i++) ans += dis[i];
    	printf("%.2lf",ans);
    	return 0;
    }     
    
    4.P1340 兽径管理

    Link

    今天上午做的题里面唯一一道比较有思维含量的题。

    首先,我们的暴力做法就是对每个都重新跑一边最小生成树,那样肯定会 (T) 飞的。

    我们其实并不需要对每次都跑一遍生成树,当我们这次要删的边不在最小生成树里面的话,那么删了这条边对答案也是没有影响的。

    因此,我们可以倒着从后往前处理,对在生成树上的边标记一下,如果删的这条边在生成树上,就暴力重新跑一边生成树。

    反之答案不变。

    一个可以优化的地方,就是当 (m < n-1) 的时候,我们直接输出 (-1) 就可以,因为这几条边是构不成一棵树的。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 1e5+10;
    int n,m,tmp;
    int fa[N],ans[N];
    struct node
    {
    	int u,v,w;
    	int tag,vis,id;
    }e[N];
    inline int read()
    {
    	int s = 0,w = 1; char ch;
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    	return s * w;
    }
    bool comp(node a,node b)
    {
    	return a.w < b.w;
    }
    int find(int x)
    {
    	if(fa[x] == x) return x;
    	else return fa[x] = find(fa[x]);
    }
    void krusal()
    {
    	tmp = 0;
    	sort(e+1,e+m+1,comp);
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	int num = n;
    	for(int i = 1; i <= m; i++) e[i].vis = 0;
    	for(int i = 1; i <= m; i++)
    	{
    		int fx = find(e[i].u), fy = find(e[i].v);
    		if(fx == fy || e[i].tag == 1) continue;
    		e[i].vis = 1;
    		fa[fx] = fy;
    		tmp += e[i].w;
    		if(--num == 1) return; 
    	}
    	if(num != 1) tmp = -1;
    }
    int main()
    {
    	n = read(); m = read();
    	for(int i = 1; i <= m; i++)
    	{
    		e[i].u = read();
    		e[i].v = read();
    		e[i].w = read();
    		e[i].id = i;
    	}
    	krusal(); ans[m] = tmp;
    	for(int i = m-1; i >= 1; i--)
    	{
    		if(i < n-1)//少于 n-1 条边构不成一棵树
    		{
    			ans[i] = -1;
    			continue;
    		}
    		for(int j = 1; j <= m; j++)
    		{
    			if(e[j].id == i+1)//找到要删除的那一条边
    			{
    				e[j].tag = 1;
    				if(e[j].vis == 1) krusal();//如果这条边在最小生成树上,就需要在重新跑一边最小生成树
    				break;
    			}
    		}
    		ans[i] = tmp;
    	}
    	for(int i = 1; i <= m; i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    2.11 Go接口内部实现
    2.08 Go之类型分支(switch判断空接口中变量的类型)
    2.08 Go之使用空接口实现保存任意值的字典
    2.07 Go之接口和类型之间的转换
    1.28 Go之接口的嵌套组合
    2.11 Go之error接口
    libpng.md
    5_中间件.md
    8_多服务运行.md
    6_模板与渲染.md
  • 原文地址:https://www.cnblogs.com/genshy/p/13694588.html
Copyright © 2020-2023  润新知