#310 (div.2) D. Case of Fugitive

1.题目描述:点击打开链接

2.解题思路:本题利用贪心法+优先队列解决。不过本题的贪心策略的选取是关键,有些看似正确的贪心策略实际上暗含危险。先说说正确的贪心策略:将所有的岛按照顺序求出第i个岛和i+1个岛之间桥的最小最大长度,并按照L从小到大排序,若相同则按照R从小到大排序。然后对桥由小到大排序,将所有的桥扫描一遍,枚举第i个桥时,将L值小于等于当前桥的区间按照(R,id)放入优先队列,R小的在队首,大的在队尾,每次看队首的R是否大于等于len,若满足,则记录答案,break,若不存在,则无解。接下来说几种经典的错误贪心策略。

错误一:将区间按照上述规则排序后,枚举区间,然后从小到大选择桥。反例:比如有2个区间,排序后是[2,31], [5,19], 桥由小到大排序后是 19,20 。按照该策略,会导致无解。

错误二:将区间按照右端点从小到大排序,若R相同,按照L从小到大排序,然后从小到大选择桥。 反例:比如有2个区间,排序后是[17,19], [2,31],桥由小到大排序后是2,17。按照该策略,会导致无解。

实际上,上述两个错误的原因的本质原因是一样的:区间端点的优先级实际上无法保证子问题得到最优解。然而为什么可以枚举桥呢?因为桥是按照长度由小到大依次枚举的,如果一个区间的L满足长度较短的桥,那么它也必然满足长度较大的桥,如果队尾的区间的R值依然小于len,那么由于后面的桥长度更大,R值更小于len了,因此这种贪心策略是正确的。

3.代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;

#define me(s) memset(s,0,sizeof(s))
#define pb push_back
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <ll, int> P;

const int N = 200000 + 10;
ll x[N], y[N];
ll length[N];
int r[N];
ll ans[N];

struct Node
{
	int id;
	ll L,R;
	bool operator<(const Node&rhs)const
	{
		return L < rhs.L || (L == rhs.L&&R < rhs.R);
	}
}a[N];

int n, m;

bool cmp(int a, int b)
{
	return length[a] < length[b];
}

int main()
{
	while (~scanf("%d%d", &n, &m))
	{
		me(a); me(x); me(y); me(length); me(r);
		for (int i = 0; i<n; i++)
			scanf("%I64d%I64d", &x[i], &y[i]);
		for (int i = 0; i < m; i++)
		{
			scanf("%I64d", &length[i]);
			r[i] = i;
		}
		for (int i = 0; i<n - 1; i++)
		{
			a[i] = { i, x[i + 1] - y[i], y[i + 1] - x[i] };
		}
		sort(a, a + n - 1); sort(r, r + m, cmp);
		priority_queue<P, vector<P>, greater<P> >q;
		int cnt = 0;
		int j = 0;
		for (int i = 0; i < m; i++)
		{
			int p = r[i];
			for (; j < n-1;j++)
			if (a[j].L <= length[p])//将左端点满足的区间入队列,同时按照R值由小到大出队列
			{
				q.push(P(a[j].R, a[j].id));
			}
			else break;
			while (!q.empty())
			{
				if (q.top().first >= length[p])//如果队首的R值≥当前桥的长度,记录答案,否则无解(但此处没有直接break,而是改为用cnt来判断)
				{
					ans[q.top().second] = p;
					cnt++; q.pop();
					break;
				}
				q.pop();
			}
		}
		if (cnt == n - 1)//如果答案的个数不是n-1,必然无解
		{
			puts("Yes");
			for (int i = 0; i<n - 1; i++)
				printf("%I64d%c", ans[i] + 1, " \n"[i == n - 2]);
		}
		else puts("No");
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 01:08:06

#310 (div.2) D. Case of Fugitive的相关文章

Round 310(Div.1) B. Case of Fugitive

Round 310(Div.1) B. Case of Fugitive Andrewid the Android is a galaxy-famous detective. He is now chasing a criminal hiding on the planet Oxa-5, the planet almost fully covered with water. The only dry land there is an archipelago of n narrow islands

贪心/思维题 Codeforces Round #310 (Div. 2) C. Case of Matryoshkas

题目传送门 1 /* 2 题意:套娃娃,可以套一个单独的娃娃,或者把最后面的娃娃取出,最后使得0-1-2-...-(n-1),问最少要几步 3 贪心/思维题:娃娃的状态:取出+套上(2),套上(1), 已套上(0),先从1开始找到已经套好的娃娃层数, 4 其他是2次操作,还要减去k-1个娃娃是只要套上就可以 5 详细解释:http://blog.csdn.net/firstlucker/article/details/46671251 6 */ 7 #include <cstdio> 8 #i

找规律/贪心 Codeforces Round #310 (Div. 2) A. Case of the Zeros and Ones

题目传送门 1 /* 2 找规律/贪心:ans = n - 01匹配的总数,水 3 */ 4 #include <cstdio> 5 #include <iostream> 6 #include <algorithm> 7 #include <cstring> 8 #include <cmath> 9 using namespace std; 10 11 const int MAXN = 2e5 + 10; 12 const int INF =

构造 Codeforces Round #310 (Div. 2) B. Case of Fake Numbers

题目传送门 1 /* 2 题意:n个数字转盘,刚开始每个转盘指向一个数字(0~n-1,逆时针排序),然后每一次转动,奇数的+1,偶数的-1,问多少次使第i个数字转盘指向i-1 3 构造:先求出使第1个指向0要多少步,按照这个次数之后的能否满足要求 4 题目读的好累:( 5 */ 6 #include <cstdio> 7 #include <iostream> 8 #include <algorithm> 9 #include <cstring> 10 #i

【CF】310 Div.1 C. Case of Chocolate

线段树的简单题目,做一个离散化,O(lgn)可以找到id.RE了一晚上,额,后来找到了原因. 1 /* 555C */ 2 #include <iostream> 3 #include <string> 4 #include <map> 5 #include <queue> 6 #include <set> 7 #include <stack> 8 #include <vector> 9 #include <dequ

Codeforces Round #310 (Div. 1) C. Case of Chocolate (线段树)

题目地址:传送门 这题虽然是DIV1的C..但是挺简单的..只要用线段树分别维护一下横着和竖着的值就可以了,先离散化再维护.每次查找最大的最小值<=tmp的点,可以直接在线段树里搜,也可以二分去找. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.

Codeforces Round #310 (Div. 1) C Case of Chocolate

思路:对于每个点而言.只与它相邻的两个点有关系.所以可以用stl或者线段树来找到它的相邻点. 代码:187ms(开挂之后貌似是最快的- -) #include <cstdio> #include <map> #include <algorithm> using namespace std; const int N = 200000 + 1; int x[N], y[N], t[N]; //适用于正负整数 template <class T> inline b

codeforce 310 div2 D题 Case of Fugitive

题目 给n个岛屿和m座桥,问桥是否足够把两两相邻的岛屿连起来,当一座桥的长度大于等于相邻两个岛屿上的点的距离最小值并且小于等于相邻两个岛屿上的点的距离最大值的时候,就可以把他们连起来. 用贪心的方法,把岛屿按照距离的最小值(即l[i+1]-r[i])排序,把桥按照长度排序,显然距离最小的岛屿应该用长度最小的桥来接,这是第一个贪心.第二个贪心是当前岛屿可以连接的所有桥中应该选距离最大值最小的.仔细想想这两个贪心思路是对的.最后看是否所有的岛屿都被连起来就行了. 代码: #include <cstd

codeforces Round #310(Div.1) 题解

嘴巴选手真爽,一不用打代码二不用掉Rating三还可以打杂.... 感觉这套题不难,但是被出题人出瞎了... 555A. Case of Matryoshkas 题目大意:给定n个大小从1到n的套娃,初始套成k坨,每次你可以选择两个操作: 1.选择一个不在任何其他套娃里的套娃,将里面的套娃取出来(要求原先里面有套娃) 2.选择一个不再任何其他套娃里的套娃,将一个套娃塞进去(要求原先里面没有套娃) 求将所有套娃合并的最小操作次数 如果一个套娃x初始在最里面,或者满足大小为1...x?1的套娃都在它