UVA1395 苗条的生成树/连续递增子序列(?)/克鲁斯卡尔



就按照区间那个理解,把并查集和克鲁斯卡尔算法都理解一下就可以了


*****************
问题导入
对于一串数字如何求他的连续递增子序列使得整个序列和最大?(应该是这个意思,,没说错)
比如,随便给你一串儿数字
1 ,5 -4,-6 5 
(1)最开始最通俗易懂也是最好想到的方法就是暴力
最基本的暴力方法是需要三个for循环
枚举左边端点l 右边端点r 里面是和 所有都算一次,那么需要0(n^3)的复杂度,如果是对于1e6的话是完全不够的


(2)我们可以尝试基本的简化来降低一点点复杂度:
预先算出Si=a1+...ai(类似打表)
(这也是O(n)的复杂度,但是写在三重for循环之外,并不影响整体
那么,只要计算 Sr- S l-1即可(保证l<=r)
(为什么是l-1呢?可以看做是一个大区间,想要得到l-r的值,用后面一直到r的减去前面l-1的,就是中间的(从l开始,一直到r的区间,得到区间就可以得到区间的和了,有了和存储一下就很方便枚举记录啦!))
(3)我们还可以进行进一步的化简
使用一个set:  set<int>h
首先来明确一下目的,我们是想要找到max{Sr -S l-1}
也就是.. 可以直接枚举一边儿啊,Sr都取一下,找到每一个r对应的最小的l-r,就可以使得整个式子取得最大值了
对于每一个r,都去找一下l-1那个最小值.....
而且而且!因为r相当于是上界,所以前面的l-1也不是很难找
而且而且而且而且!!!不仅是上界,还可以充当max{Sr -S l-1}里面的前面那个Sr,岂不是更好!
(4)具体操作:
你看啊!!
for(int i=1;i<=n;i++)(z这里的i其实是r,其实也就是上界
p=find min(S l-1)
(实现的话,,,,如果用set插入的话,,,,直接int a=*h.begin()就可以了,其实也是迭代器,但是*的话可以直接用成这样)


h.insert(S i-1)
ans=max(ans,Si -p)(这里的Si是枚举出来的Sr,而p是在这个r的上界之内,最小的Sl-r,这样就可以保证ans是最大的那个了)




set是O(logn)的..很好!..


(6)终极  ..  O(n)的好方法 一下子就完了
直接记录一下ans....从第一个开始加,每一次的答案都塞到set里面去。
如果是正的那就加了塞进去,如果是负的那就减了塞进去,如果是负数的话那就置为0(很重要的哦!其实也是可以重新开始了)
最后找到最大的就可以了......
(应该是这样的吧?)


(7)克鲁斯卡尔算法:
Kruskal算法
https://www.cnblogs.com/yoke/p/6697013.html
理解了过程了...代码还没写过=.=
大概就是:
(利用并查集)
先按照边权排序,对于边和边权,v u,如果加入了没形成环,就加入使得形成 连通图,如果加入了形成了环就扔掉不要了
直到所有的点都被连通了为止...
它依赖并查集来实现的:
(不知道是不是所有的连通图都要这样实现....反正按照并查集嘛,如果你是在一个部落里的就会形成环,然后放弃这个决定/如果不是的话就把你俩连起来)
..真便利啊
*************
(8)上面是最小生成树
下面是苗条生成树
这个题目的意思是首先给定了你点的数量和边的数量  然后输入
让你找到  所有的连通的图里面,边权值最小的那个,(也就是 最大的减最小的   差最小的那个 就是最苗条的生成树)


那么做起来还需要一点小心思:
(以下几行为参考:https://blog.csdn.net/qq_32866009/article/details/52694005)


中文题意:给出一个n(n<=100)结点的图,求苗条度(最大边减最小边的值)尽量小的生成树。 
【分析】首先把边按权值从小到大排序。对于一个连续的边集区间[L,R],如果这些边使得n个点全部连通,则一定存在一个苗条度不超过W[R]-W[L]的生成树(其中W[i]表示排序后第i条边的权值)。 
从小到大枚举L,对于每个L,从小到大枚举R,同时用并查集将新进入[L,R]的边的两端的点合并成一个集合,与Kruskal算法一样。当所有点连通时停止枚举R,换下一个L(并且把R重置为L)继续枚举。


然后代码我是看的这一份:
https://blog.csdn.net/u013509299/article/details/39403747
其实都理解完了也不复杂,有一点需要注意的:
它其实也是在枚举区间哦....对于[L R ]这里面...
如果还没跑完,就形成了连通的树了(n=cnt-1)
然后,可以继续跑

只是按照边权进行了排序,然后从start开始往下枚举

(好了,我应该不太懂,评论区有待补充)
#include<iostream>  
#include<algorithm>  
#include<stdio.h>  
#include<math.h>  
#include<cstring>  
#include<string>  

#define N 105  
#define inf 0x3f3f3f3f  
#define pi acos(-1.0)  
#define eps 10e-6  
using namespace std;
struct node
{
	int x, y, w;
}s[N*N];//105*  105
bool cmp(node a, node b)
{
	return a.w < b.w;
}
int f[N];
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
//return x = f[x] ? x : f[x] = find(f[x]);
int main()
{
	int n, m;
	while (scanf_s("%d%d", &n, &m) != EOF)
	{
		if (n == 0 && m == 0)    break;
		int i;
		for (i = 0; i < m; i++)
			scanf_s("%d%d%d", &s[i].x, &s[i].y, &s[i].w);
		sort(s, s + m, cmp);
		//仅仅是按照w排序了而已
		s[m].w = -1;
		int ans = inf;
		int start = 0;
			int end;
		while (start < m)
		{
			for (i = 0; i <= n; i++)
				f[i] = i;
			int cnt = 0;
			for (i = start; i < m; i++)
			{
				int xx = find(s[i].x);
				int yy = find(s[i].y);
				if (xx != yy)
				{
					cnt++;
					f[xx] = yy;
				}
				if (cnt == n - 1)
					break;
			}
			if (cnt == n - 1)  
				ans = min(ans, s[i].w - s[start].w);
			else break;
			while (s[start].w == s[start + 1].w)
				start++;
			start++;
		}
		if (ans == inf)  printf("-1\n");
		else printf("%d\n", ans);

	}

	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值