题目链接:http://acm.zju.edu.cn/onlinejudge/showProblems.do?contestId=1&pageNumber=32
题意
在一个平面上给你n条线段的左右端点的横坐标(可重叠),纵坐标为当前是第几条,即两点坐标为(l,i)和(r,i),你可以在平面的任意整数坐标位置放置一个标记,标记的横坐标不可相同.问最给定的线段最多会被标记多少条
思路
比赛时不会,回来的动车上和张dalao讨论出结果- -
首先,一开始想到的是贪心,根据需要排序这一点想到是使用堆(优先队列)操作
根据贪心的思想,每次都应该选左端点l值最小(若左端点相同则应选右端点小的,即区间大小比较小)的放置标记,这样放置多的可能性高
每次放置一个标记后,应将左端点与标记位置相同的线段左端点进行更新(即加一)
就比如(1,3),(1,1),(2,2),若不进行排序直接判断,(1,2)放置1,(1,1)无法放置,(2,2)放置2.若只在计算前进行一次排序(贪心),则为(1,3)放置2,(1,1)放置1,(2,2)无法放置,而实际上最优结果为(1,3)放置3,(1,2)放置1,(2,2)放置2.所以使用堆可以轻松解决,应该还算是挺好想明白的.(比赛时真的是傻了,打完后想到这个解法更是傻了,要是这题做出来就是个银牌了,不应该倒在这么简单的题上,还是太菜了啊= =)
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <map>
#include <stack>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int MAXN = 1e5+10;
const int MOD = 1e9+7;
struct Node
{
int l;
int r;
Node(int l=0, int r=0):l(l),r(r){}
};
bool operator < (Node a, Node b) //重载运算符
{
if(a.l > b.l) return 1;
else if(a.l == b.l)
if(a.r>=b.r)
return 1;
return 0;
}
int main()
{
int T;
int n;
int now;
int l,r;
int ans;
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
priority_queue<Node> pq; //优先队列
for(int i=0; i<n; i++) {
scanf("%d%d",&l,&r);
pq.push(Node(l,r));
}
Node node;
now = ans = 0; //将初始放标记位置置为0
while(!pq.empty()) {
node = pq.top();
pq.pop();
if(now <= node.r) { // 判断当前标记位置是否小于等于队首元素右端点
ans++;
int tmp = node.l;
if(now < node.l) { //判断当前标记位置是否小于队首元素左端点
now = node.l+1; // 若小于,则更新为队首元素左端点加一
}
else {
now++; // 否则直接加一
}
while(!pq.empty() && pq.top().l==tmp) { //对所有与队首元素左端点相等的元素左端点进行更新
node = pq.top();
pq.pop();
node.l++;
if(node.l <= node.r) { // 左端点小于等于右端点才重新加入队列
pq.push(node);
}
}
}
}
printf("%d\n",ans);
}
return 0;
}