第十三周贪心算法总结

这周呢,主要就是在看老师布置的博客内容,全是关于贪心的,而且都是很厉害的大佬写的,思维性强。我读题的时候是读了老半天才看懂说了个什么情景,然后再分析具体的实现思路,思想都很巧妙新颖。下面进行详细的总结。

D. Gadgets for dollars and pounds,这个题呢,大概是说,一共有n天,m件商品,要买k个物品,目前有一定数量金币,金币不能直接用,需要先进行转换成等价的货币,输入两种货币的汇率,然后每个物品还有两种属性,1代表用第一种货币,2表示需要用第二种货币,题目问的是最少需要多少天购买到不同种类的k个物品。感觉题意挺长的,这个题的思想就是二分和贪心。我理解的是,汇率比较低的那一天,可以转化成更多的货币,这样钱数最多,可以一次性买很多东西,可以尽可能的把时间提前。一天可以尽可能的多买物品,一个物品只能买一次,如果某一天可以买全所有物品了,可以再往前推一天,看看是不是能买到,如果不能买到,可以再尝试往后推一天,直到有符合要求的天数。可以把每件物品的花费升序排列一下,选花费小的来买,不要超过给定的范围。为了降低复杂度,可以用二分的思想来排序,就可以解决了。

【HDU5527 2015长春赛区A】,题意是说,给定一定数量的面值分别为1,5,10,20,50,100,200,500,1000,2000 的货币,问最多使用多少枚硬币,可以恰好使货币价值总和为指定的钱数。一开始我看错这个题了,题目意思比较简短,我还以为是要求最少的货币数量,但是这个题目反过来了,问最多需要多少货币。这个题的思想是贪心和特判,首先可以想到的是,要尽可能的从面值较小的货币开始拿,如果小的不够了,再往大的地方拿。一开始,我觉的这不思路挺简洁的吗?这是个金牌题,再仔细想想就能发现,如果只是简单这样想肯定是有问题的,每次都从面值小的货币开始取,然后再考虑取面值大的货币,大的方向是对的,但是值得注意的是,这样的取法不一定能保证最后凑起来一定整好等于给定的货币的价值,最后可能就不够了,只剩下面值大的货币,而小的一开始都被取完了,这是比较难处理的。而且题目给定的货币的种类,20的和50的货币不满足倍数关系,而且500的和200的货币也不满足倍数关系,需要解决这两个特殊关系。如果50和500的是偶数对儿个取的话,就实现了一个转化,就不存在这样的矛盾问题了。然后可以枚举,如果这两个面值的货币有奇数个,可以先取一个,转化成偶数个,之后就成对儿的一块取,就不存在这一组特殊关系无法处理的情况了,就当于实现了一个转化,把本来矛盾的地方通过成对儿的处理,转化成合理的容易处理的情况。
核心部分代码:

void tryit()
{
	if (e[5] > a[5] || e[8] > a[8])return;
	MS(b, 0);
	int num = e[5] + e[8];
	UI tmp = e[5] * 50 + e[8] * 500;
	for (int i = 1; i <= 10; i++)
	{
		if (e[i] == -1)
		{
			num += a[i];
			b[i] = a[i];
			tmp += a[i] * c[i];
		}
		else
		{
			b[i] = a[i] - e[i];
			if (b[i] & 1)--b[i];
			num += b[i];
			tmp += b[i] * c[i];
		}
		if (tmp >= m)
		{
			int more = tmp - m;
			int over = cntmin(more, i);
			if (~over)
			{
				num -= over;
				gmax(ans, num);
			}
			return;
		}
	}
}

D. Hamiltonian Spanning Tree,这个题呢,自己理解的不是特别的明白。题意是说在一个图中,有n个点,点与点之间的距离都是一样的,给定一棵n个点的生成树,每条边的权值也是一定的,问如何遍历n个点,使得走过的路程最小,每个点仅遍历一次。这个题的思想是DFS,数的最小链划分再加上贪心思想。题解大体的思路是说,运用树的二分图性质,比较生成树的边更优还是非树边更优,如果非树边更优,先走二分图一侧的点,再走二分图另外一侧的点,两侧之间选一条非树边连接;如果生成树的边更优,尽量走生成树的边。可以把题意转化为,这棵树可以划分成多少棵不同的链,找出最小链划分,利用DFS做黑白染色,然后再二分匹配,匹配数比n-2少了多少,就多出几条树外边。如果是个单链树的话,额外链数为0,如果有分叉,保留一个,其余的向上分配分叉树,分叉数为两个的话就不用了。感觉这个思路还是挺复杂的,还是得多理解才行。

E. Anton and Ira,这个题是说给定两个长度为n的全排列,对第一个数组进行操作交换任意两个位置上的数,交换一次的成本为两个位置的差的绝对值,问如何操作可以把第一个全排列变成第二个。这个题用到了构造思想,可以把第二个全排列看成一个递增的序列,把交换的操作看成两个移动的操作,一个位置左移,一个位置右移。贪心思想,就是每个元素都往目标位置移动,一步步的靠近目标位置,结果肯定为最优,如果反着移动就会远离目标位置,就不是最优了。可以考虑每个元素,假设一个元素在当前的某个位置,那么从头开始一直到当前位置,肯定会有一个元素的位置是大于等于当前位置的,这样就可以进行交换,这样一步步的移动,直到所有元素都回到正确的位置。感觉这个思想比较抽象一些,但是也很巧妙。
核心部分代码:

for (int i = 1; i <= n; i++)
{
	if (go[i] != i)
	{
		int j = from[i];
		while (go[i] != i)
		{
			int k; for (k = i; k <= n; k++)if (go[k] >= j)break;
			c[++tim] = j;
			d[tim] = k;
			int tmp = go[j];
			go[j] = go[k];
			go[k] = tmp;
			j = k;
		}
	}
}

Codeforces Round 328 (Div 2)D Super M,这个题是说,给定一棵含有n个节点的树,要到的点有m个,可以选择传送到这棵树的任意一点,使得到达m个点后的路程要尽可能小,问可以传送到的点是哪个点,如果有多个解,输出编号最小的那个点,也要输出最小的距离之和。可以以这些点中的任意一点为根节点,统计所有含有关键点的最小路径的二倍,沿着环走过所有关键结点,通过树的直径两遍DFS求得距离最远的两个点之间的路,可以少走这一段路。还要保证字典序最小,首先从任意一个点开始搜最长路,搜到路径最长的点中编号最小的那个x,然后再从这个x开始搜最长路,搜到路径最长的点中编号最小的y ,取最小值即可。(参考题解)
核心部分代码:

void dfs(int x, int dis)
{
	e[x] = 1;
	if (imp[x])
	{
		if (dis > DIS)
		{
			DIS = dis;
			ED = x;
		}
		else if (dis == DIS)gmin(ED, x);
	}
	f[x] = 0;
	for (int i = a[x].size() - 1; ~i; --i)
	{
		int y = a[x][i];
		if (e[y])continue;
		dfs(y, dis + 1);
		if (imp[y])
		{
			imp[x] = 1;
			f[x] += f[y] + 1;
		}
	}
}
void dfs_(int x, int dis)
{
	e[x] = 0;
	if (imp[x])
	{
		if (dis > DIS)
		{
			DIS = dis;
			P = x;
		}
		else if (dis == DIS)gmin(P, x);
	}
	for (int i = a[x].size() - 1; ~i; --i)
	{
		int y = a[x][i];
		if (!e[y])continue;
		dfs_(y, dis + 1);
	}
}

总结:
总体感觉吧,贪心主要的还是思维,思路如果清晰,可以用最少的代码解决问题,即使代码很长,复杂度也不会很高,可以很好的处理问题。一些贪心思维也都很巧妙,思考的也非常深,确实得好好思考,才能明白具体是怎么实现贪心的,像是那一道金牌题,题意很明确,但是不好实现,但是如果明确好了贪心方案,就能很好的解决。而且贪心也可以结合其它知识结合进行综合出题,生成树,搜索,状态压缩等等,都可以结合在一块。

目前主要还是在看博客,总结思路,相信以后再做题的时候可以有相关的思路去解决类似问题。

这一周比较忙碌,同时也结束了贪心专题,下周就要看DP的博客了。DP主要还是那个状态方程不好写,要保证每一步都是最优,从而整体最优。这一周先接触了DP的一些概念,所以对这一部分内容先不做详细叙述了,下周自己看DP博客的时候,再把详细的内容以及博客里的一些思路一块儿总结一下。

好啦,下周继续努力!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

暗紫色的乔松(-_^)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值