给定N段互不相交且有序的区间,用M座桥把他们连接成一段区间。如果存在相邻的两个区间分别有两个点X,Y满足|Y-X| = Li,Li是第i座桥的长度,那么就可以用第i座桥把这两个区间连接起来。问这M座桥是否可以将他们连接成一段区间,如果可以,输出任意一组合法的方案。
思路
考虑相邻的两个区间[Li,Ri],[Li+1,Ri+1],可以连接他俩的桥的范围为[Li+1-Ri,Ri+1-Li],现在题目变成了给定N段区间和M个点,问每个区间是否可以不重复的覆盖一个点,这个问题可以用贪心解决:将所有区间按照右端排序,依次给每个区间覆盖最靠左的一个未被覆盖过的点,如果一个区间无法找到可以覆盖的点,那么输出无解。
贪心的证明如下:
对于区间i,存在两个可以覆盖的点X,Y,且有X < Y,如果区间i覆盖点Y合法,那么区间i覆盖X也合法,贪心证毕。对于任意区间K,K的右端大于 i的右端,若之前K可以覆盖Y之前的点合法,那么现在K也可以覆盖Y合法;若之前K可以覆盖Y之后的点合法,那么现在依然可以覆盖。调整成功,证毕。
比赛的时候想到了这个方法,然而还是太菜了T^T。赛后用multiset重载 < 号过了这道题,STL用得太差。。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define bit_cnt(x) __builtin_popcount((unsigned)x)
#define foru(i, a, b) for(int i=(a); i<=(b); i++)
#define ford(i, a, b) for(int i=(a); i>=(b); i--)
#define clr(a, b) memset(a, (b), sizeof(a))
typedef long long ll;
const double Pi = 4 * atan(1.0);
inline int readint() {
char c = getchar();
while(!isdigit(c)) c = getchar();
int ret = 0;
while(isdigit(c)) ret = ret * 10 + c - '0', c = getchar();
return ret;
}
/**************************************************
Header
***************************************************/
const int N = 200010;
struct line{
ll st, en;
int idx;
}L[N];
struct point{
ll x;
int idx;
bool operator <(const point B)const{
if (x < B.x) return 1;
return 0;
}
}tmp;
int n, m, g[N];
ll preL, preR, nowL, nowR;
bool cmp(line A, line B){
if (A.en < B.en) return 1;
return 0;
}
multiset<point> Set;
multiset<point>::iterator it;
bool solve(){
foru(i, 1, n){
if (! Set.size()) return 0;
tmp.x = L[i].st; tmp.idx = -1;
it = Set.lower_bound(tmp);
if (it == Set.end()) return 0;
if ((*it).x > L[i].en) return 0;
g[L[i].idx] = (*it).idx;
Set.erase(*it);
}
return 1;
}
int main(){
//freopen("B.txt", "r", stdin);
while (scanf("%d %d", &n, &m) != EOF){
n --;
scanf("%I64d %I64d", &preL, &preR);
foru(i, 1, n){
scanf("%I64d %I64d", &nowL, &nowR);
L[i].st = nowL - preR;
L[i].en = nowR - preL;
L[i].idx = i;
preL = nowL; preR = nowR;
}
sort(L + 1, L + 1 + n, cmp);
Set.clear();
foru(i, 1, m){
scanf("%I64d", &tmp.x);
tmp.idx = i;
Set.insert(tmp);
}
if (! solve()) puts("No");
else {
puts("Yes");
foru(i, 1, n-1) printf("%d ", g[i]);
printf("%d\n", g[n]);
}
}
return 0;
}
重载set
struct point{
ll x;
int idx;
bool operator <(const point B)const{
if (x < B.x) return 1; // 这是multiset的写法
return 0;
}
};
删除的时候
multiset<point>::iterator it
,
multiset.erase(it)
multiset单点删除要删迭代器
struct point{
ll x;
int idx;
bool operator <(const point B)const{
if (x < B.x) return 1;//这是set的写法
if (x == B.x && idx < B.idx)
return 0;
}
};
需要注意的是由于set不能有重复的元素,然而struct中有两维
如果没有重载第二维的比较,那么第一维相同的点只会留下一个