贪心&树图基础&最小生成树

(一)贪心算法:

总是做出当前看来最好的选择,某种意义上的局部最优解。
核心是去寻找一种解决问题的方法,如果策略正确,那么往往是易于描述、易于实现的。
一、最优装载问题
给出n个物体,第i个物体重量为wi。选择尽量多物体,使总重量不超过C。
只关心数量,装重的没有装轻的划算。

二、部分背包问题
有n个物体,第i个物体的重量为wi,价值为vi,选若干个物体,使在总重量不超过c的情况下总价值最大。这里的每个物体都可以只取走一部分,价值和重量按比例计算。
综合考虑重量和价值两个因素,计算出每一种物体单位重量的价值,然后按单位重量的价值从大到小排序,优先选择比较大的物体。

三、乘船问题
进行一次独木舟的旅行活动,独木舟可以在港口租到,并且之间没有区别。一条独木舟最多只能乘坐两个人,且乘客的总重量不能超过独木舟的最大承载量。我们要尽量减少这次活动中的花销,所以要找出可以安置所有旅客的最少的独木舟条数。现在请写一个程序,读入独木舟的最大承载量、旅客数目和每位旅客的重量。根据给出的规则,计算要安置所有旅客必须的最少的独木舟条数,并输出结果。

考虑最轻的人i,他应该和谁一起坐。如果每个人都无法和他一起坐船,则唯一的方案就是每人坐一艘船。否则,他应该选择能和他一起坐船的人中最重的一个j。这种方法是贪心的,因为它都让眼前的浪费最少。

四、选择不相交区间
数轴上有n个开区间(si,fi)。选择尽量多个区间,使得这些区间两两没有公共点。
策略:排序使得f1<=f2<=…<=fn,从前向后挑选。
证明方法:
(1)归纳法
证明贪心法得到最优解叙述一个描述正确性的命题对算法步数归纳或者对问题规模归纳
归纳基础:证明存在最优解包含活动1
归纳步骤:假设按照算法前k 步选择都导致最优解,证明第k+1步选择也导致最优解
归纳步骤的证明思路:
1.算法第k 步选择活动i1=1,i2, … , ik,根据归纳假设,存在最优解A={i1=1,i2, …,ik}∪B是剩下的待选活动集S’的一个最优解
2.由归纳基础,存在S’的最优解B’ 包含ik+1
3. 由|B’|=|B|知A’={i1=1,i2, … , ik}∪B’最优
4.A’={i1=1,i2, …,ik,ik+1}∪(B’−{ik+1})最优

(2)交换论证(不会更差)
在保证最优性不变的前提下,从一个最优解进行逐步替换,最终得到贪心法的解。
定理1:算法Select执行到第k 步, 选择k 项活动i1= 1,i2,…,ik,那么存在最优解A包含i1=1,i2, … , ik根据定理:算法至多到第n 步得到最优解
在这里插入图片描述
在这里插入图片描述

【例题】MainSequenceCodeforces286C
定义幸运数列:
空数列是幸运数列
如果S是幸运数列,那么{r, S, -r}也是幸运数列(r > 0)
如果S 和T都是幸运数列,那么{S, T}也是幸运数列
给定一个幸运数列中每个数的绝对值,并且要求其中的一些数是负数,其他的可正可负。问是否有合法方案,如果有,给出任意一种方案。 N ≤ 106
幸运数列例子:{1, 2, -2, -1, 1, -1, 1, -1}
输入:1 1 1 1
要求第三个数是负数输出:1 1 -1 -1

实质上就是给两个相同的数进行配对,且两对数不允许交叉。最直观的想法就是能否将栈顶元素与其配对。

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int N=1000005;
int n,m;
int a[N],vis[N];
int st[N],top;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int t;
		scanf("%d",&t);
		vis[t]=1;
	}
	for(int i=n;i>=1;i--){
		if(!top) st[++top]=i;
		else{
			if(a[st[top]]==a[i]){
				if(vis[st[top]]&&!vis[i]) top--;
				else if(!vis[st[top]]&&!vis[i]){
					vis[st[top]]=1;
					top--;
				}
				else st[++top]=i;
			}
			else st[++top]=i;
		}
	}
	if(!top){
		printf("YES\n");
		for(int i=1;i<=n;i++){
			if(vis[i]) printf("-%d ",a[i]);
			else printf("%d ",a[i]);
		}
	}
	else printf("NO\n");
	return 0;
}

(二)贪心应用:最小生成树
任意一个无向图一定有偶数个奇点。

完全图:
无向图中,任意两个顶点都存在一条边,称为完全图。
有向图中,任意两个顶点都存在两条方向相反的边,称为完全图。n阶完全有向图含有n*(n-1)条边,n阶完全无向图含有n*(n-1)/2条边。

连通图:
无向图中,如果从u到v有路径,则称u和v是连通的。如果对于图中任意两个顶点u和v都是连通的,则称图是连通图,否则称为非连通图。
有向图中,如果对于任意两个顶点u和v,从u到v和从v到u都存在路径,则称图为强联通图。

存储方法比较:

邻接矩阵:(点很少,边很多)
优点:查找/删除某条边O(1)。
缺点:遍历某一点的邻居是O(V);空间复杂度很大,O(V*V)。

邻接表:
优点:快速遍历某点的所有邻居;占用存储空间小,是O(边数),在稀疏图(m<<n2)上的效率远胜于邻接矩阵。
缺点:查找/删除边不是常数时间。

树:
任意两个顶点间有且仅有一条路径的图,没有环的连通图,有n-1条边的连通图。

树的重心:
树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。换句话说,删除这个点后最大连通块(一定是树)的结点数最小。

树的重心的性质
树中所有点到某个点的距离和中,到重心的距离和是最小的。
把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
一棵树最多有两个重心,且相邻。

【树的重心例题】会议
(洛谷 P1395)
先找出树的重心,然后dfs求出路径长度。
注意比较mxs[u]和mxs[pos],要保证pos尽可能小,所以可以用make_pair(),比较两个pair(按两个关键字比较,如果mxs[pos]==mxs[u],那么取pos和u中较小的一个)。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=50005;
int n,pos,tot,ans;
int head[N],size[N],mxs[N];
struct node{
	int to,next;
}edge[N<<1];
void add(int u,int v){
	edge[tot].next=head[u];
	edge[tot].to=v;
	head[u]=tot++;
}
void DFS(int u,int fa){
	size[u]=1; mxs[u]=0;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa) continue;
		DFS(v,u);
		size[u]+=size[v];
		mxs[u]=max(mxs[u],size[v]);
	}
	mxs[u]=max(mxs[u],n-size[u]);
	if(make_pair(mxs[u],u)<=make_pair(mxs[pos],pos)) pos=u;//
}
void dfs(int u,int fa,int dep){
	ans+=dep;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa) continue;
		dfs(v,u,dep+1);
	}
}
int main(){
	mxs[0]=0x7f7f7f;
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v); add(v,u); 
	}
	DFS(1,-1);
	dfs(pos,-1,0);
	printf("%d %d",pos,ans);
	return 0;
}

生成树:

在图论的数学领域中,如果连通图G的一个子图是一棵包含G 的所有顶点的树,则该子图称为G的生成树(SpanningTree)。

生成树是连通图的包含图中的所有顶点的极小连通子图。图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树。

对于一个带权图,其生成树中,边权值和最小的被称为最小生成树

一、Prim算法:
在这里插入图片描述
时间复杂度O(n2)
利用优先队列+邻接表存图,可以优化到O((n+e)logn)

二、Kruskal算法:
在这里插入图片描述在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值