第十二周ACM博客总结

这是第十二周周末了,感觉时间过的还挺快的。这周呢,自己主要还是在做最短路和小生成树的题目,并且在博客资料中总结了一些贪心和最短路的经典例题和思路方法。上周最小生成树的内容整理了一些,但是没有整理最短路径的内容,自己先把这块内容整理了一下。下面是详细的总结。


最短路径

最短路径讨论的经典问题就是,给定任意图和起点终点,如何求从起点到终点的最短路径。常用的算法有Dijkstra,Bellman-Ford,SPFA和Floyd算法。

Dijkstra基本思想对图设置集合存放被访问的顶点,然后每次从集合中选择与起点的最短距离最小的顶点,访问并且放在集合里边,然后让那个小顶点作为一个中介,优化起点与所有从小顶点能到达的顶点之间的最短距离,直到最后集合已经有所有的顶点,但Dijkstra解决不了出现负权边的情况。

BF算法和Dijkstra算法一样,设置数组存储从源点到达各个顶点的最短距离,BF返回bool值,如果存在从源点可到达的负环,返回false,否则返回true,数组中存放的值就是从源点到达各个顶点的最短距离,思路更加简洁一些,但更加优化的是SPFA算法,可以结合DFS,优先队列,STL等进行综合判断。Floyd可以解决全源最短路问题,可以求任意两点之间的最短路径长度,用邻接矩阵实现,顶点数少的话可以考虑使用,因为复杂度较高。一般常用的感觉还是Dijkstra算法和SPFA算法,做题的时候也是先考虑这两个思路。

经典例题总结

贪心
135. 分发糖果,这个题我想了好久才大体明白了处理的过程,题意就是说给孩子们分发糖果,每个孩子都对应一个评分,每个孩子至少一个糖果,要求相邻两个孩子评分更高的孩子会获得更多的糖果,问需要的最少糖果数。题意很好懂,这个题的的难点之处在于如何比较一个孩子两边的情况,是两边都一块比较,还是一边一边的考虑。最优的解法就是先确定一边后再确定另一边,要不然会顾不过来,先确定右边大于左边的情况,从前向后遍历,只要右边评分比左边大,右边的就多一个糖果;再确定左边大于右边的情况,从后向前遍历,如果左边的大于右边的,此时左边的孩子的糖果数量可以是右边孩子糖果数量加上1,也可以是之前第一次遍历得到的糖果数量,取两者的最大值,保证既大于左边也大于右边,确保是最优解,然后局部最优了,整体就是最优了。这个题重要的是明确贪心思路。
核心代码如下:

t candy(vector<int>& ratings) {
        vector<int> candyVec(ratings.size(), 1);
        // 从前向后
        for (int i = 1; i < ratings.size(); i++) {
            if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
        }
        // 从后向前
        for (int i = ratings.size() - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1] ) {
                candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
            }
        }
        // 统计结果
        int result = 0;
        for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];
    }

最短路
有一类题型是拆点问题,题意是说给定一个n个点,m条边的无向图,边上有一个编号,表示这条边归编号公司管理,如果相邻的两条边的编号相同,则代价为0。 否则代价加1, 问1到n的最低代价为多少。感觉这个题挺巧妙的,先用图来模拟一下转化过程。圆圈代表城市,编号代表公司
拆点前:
在这里插入图片描述

拆点后:
在这里插入图片描述

题目是让求权值,我们就把图转化成为带相应权值的图,本来边是代表公司,现在代表权值0或1就好分析了。括号内,第一个代表城市,第二个代表公司,这样相同公司之间代价为零,这条边的权值就是0,否则是1。接下来用Dijkstra算法跑一遍就可以了,代码和之前的差不多的,主要的还是图的转化过程,转化好了解决起来就容易了。

还有一类题是删点问题,题意是说给定一个图,删除除了起点和终点的任意一点,使得起点和终点不能联通,如果不能实现,就要在寻找删除任意一点的情况下使得最短路径的值最大。可以想到的是,只要删除一个点,那么与它相连的所有边都相当于没有了,需要注意的是进行下个点删除前,要还原上一个点,最后用Dijkstra算法解决就可以了。

洛谷题目总结

P6770 [USACO05MAR]Checking an Alibi 不在场的证明,这个题目感觉挺典型,结合Dijkstra算法用到了链式前向星堆优化,可以很好的提高代码的效率。链式前向星就是静态建立的邻接表,存储的就是以1到n为起点的边的集合。堆优化呢,就是说用堆、用优先队列维护最小的点,从而减少复杂度。其余的按照模板来就可以了。 P4779 【模板】单源最短路径(标准版),这个题的思想和上边这个基本一致。
核心代码如下:

struct Edge {
	int next;
	int to;
	long long int w;
}e[maxm]; 
int head[maxn];
void add_edge(int u, int v, int w)
{
	e[cnt].to = v;
	e[cnt].w = w;
	e[cnt].next = head[u];
	head[u] = cnt++;
}

int vis[maxn];
int dis[maxn];

struct node {
	int pos;
	int dis;
	bool operator <(const node& y)const {
		return dis > y.dis;
	}
};
priority_queue<node> q;
void dijkstra(int s)
{
	
	for (int i = 0; i <= n; i++)
	{
		dis[i] = INT_MAX;
	}
	dis[s] = 0;
	q.push((node) { s, 0 });
	while (!q.empty())
	{
		node tmp = q.top();
		q.pop(); 
		int x = tmp.pos;
		int d = tmp.dis;
		if (vis[x]) continue;
		vis[x] = 1;
		for (int i = head[x]; i != -1; i = e[i].next)
		{
			int y = e[i].to;
			if (dis[y] > dis[x] + e[i].w)
			{
				dis[y] = dis[x] + e[i].w;				
				q.push((node) { y, dis[y] });
			}
		}
	}
}

P1821 [USACO07FEB] Cow Party S,这个题目也很有技巧性,题目的意思是说农场之间有m条有向路,有一定长度,参加完派对必须回家,求n头牛一个来回中最长的一条路径长度。要保证去的是最短路,回来的时候也是最短路,并且输出最长的那一条路,两条路径都是独立的。有一个很巧妙的方法,就是可以把所有边反向,所有边反向之后,出发的最短路就转化为以派对农场为源点的最短路问题。将所有边反向后,以派对农场为源点求最短路径,然后保持原有边的方向,同样以派对农场为源点求一次最短路径,得到每头牛回家时的最短路,具体实现的时候也可以用链式前向星和优先队列进行优化同时呢,这个题也可以采用置换矩阵进行思路的优化,将问题转化为求从派对农场到其它农场的最短路,表达方式不大一样,但思路是一样的。

P1629 邮递员送信P1342 请柬,这两个题目都要正反向都要跑一次Dijkstra算法或者SPFA算法才能解决,其余的题目大部分全是模板,当然了,这部分题目都是基于模板来的。

总结:

总体来说吧,最短路最小生成树这个专题模板比较多,思想是固定的,在此基础上要进行一些优化,比如说用堆优化、链式前向星优化代码,来提高代码的效率。还有一些常用技巧,比如说有些问题需要判断负环,将边反向,或者说前后都要跑一遍相应算法等等。贪心重要的是想好贪心策略,以局部最优达到整体最优。下周就要集中精力看贪心的博客内容了,好像全是大佬写的博客,难度好像挺大,希望下周我可以把它整明白!

下周继续努力吧!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗紫色的乔松(-_^)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值