codevs P1214 线段覆盖

听作者讲骚话

说真的我很反感某些blogs扔一个代码就叫题解。
好歹也放个题目啊这样就不用搜了
水遁——水一个博客之术!

线段覆盖问题

题目描述 Description:
给定x轴上的N(0<N<100)条线段,每个线段由它的二个端点a_I和b_I确定,I=1,2,……N.这些坐标都是区间(-999,999)的整数。有些线段之间会相互交叠或覆盖。请你编写一个程序,从给出的线段中去掉尽量少的线段,使得剩下的线段两两之间没有内部公共点。所谓的内部公共点是指一个点同时属于两条线段且至少在其中一条线段的内部(即除去端点的部分)。

输入描述 Input Description:
输入第一行是一个整数N。接下来有N行,每行有二个空格隔开的整数,表示一条线段的二个端点的坐标。

输出描述 Output Description:
输出第一行是一个整数表示最多剩下的线段数。

数据范围及提示 Data Size & Hint:
0<N<100

样例输入 Sample Input样例输出 Sample Output
32
6 3
1 3
2 5

时间限制: 1 s
空间限制: 128000 KB

最初想法

扔了个朴素算法,AC了6个,剩下的都是WA
最初的想法很傻很暴力(其实就是题目用大标题贪心骗我),所以我就一个劲儿往贪心想,加之人特别笨,所以。。。。。。
总之我计算每一个线段的
这是错误的WA代码,又臭又长:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxm=1001; 
int n,maxn,maxo,cnt;
bool flag=0;
struct node{
	int order,l,r,num;
	node(){order=l=r=num=0;return;}
}d[maxm];

bool in(int l,int r,int tl,int tr){ 
	if(l<tl&&tl<r) return 1;
	if(l<tr&&tr<r) return 1;
	if(tl<l&&l<tr) return 1;
	if(tr<r&&r<tr) return 1;
	return 0;
} 

int main(){
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>d[i].l>>d[i].r;
		if(d[i].l>d[i].r) swap(d[i].l,d[i].r);
		d[i].order=i;
	}
	
	do{
		flag=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(in(d[i].l,d[i].r,d[j].l,d[j].r))
					d[i].num++;  //统计线段i交点数
			}
		}
		
		for(int i=1;i<=n;i++){
			if(d[i].num>maxn){  //找出最多的交点的线段编号
				flag=1;
				maxo=i;  //记录编号
				maxn=d[i].num; //记录最大交点数
			}
			d[i].num=0;
		}maxn=0;
		
		if(flag){  
			d[maxo].l=-10000;  //删除此线段
			d[maxo].r=-10000;
			cnt++;  //顺理成章的加一
		}maxo=0;
		
	}while(flag!=0);  //直到无法继续更新最大交点数退出循环
	
	cout<<n-cnt<<endl;
	return 0;
}

其实我就是根据每一个线段的范围,然后计算这个线段跟其它线段的交点有几个,然后交点最多的线段优先被去掉。最后,把总的线段减去删掉的线段就是剩余的线段。
当然了我至今想不懂为什么会WA(那你晒出来干嘛

干嘛?

因为我很SAO!!!!!!!!!!!!!

正解

因为愚蠢的我想不懂。
所以我很聪(wei)明(suo)地去找了题解。

然后发现原来大家都是这种写法,WOC用的变量都是一样的。(32%的AC率原来如此[手动滑稽]
撸一份代码给你,这是可以AC的

#include<iostream>
#include<algorithm>
using namespace std;
typedef struct{
    int a,b ;
    bool operator < (const node &x,const node &y){
        return x.b<y.b;
	}  
}node;

int main(){
    int n;
    node a[10000];
    cin>>n;
        for(int i=0;i<n;i++){
            cin>>a[i].a>>a[i].b;
            if(a[i].a>a[i].b) swap(a[i].a,a[i].b);
        }
        
        sort(a,a+n);
        
        int count=1;
        int end=a[0].b;
        for(int i=0;i<n;i++)
            if(end<=a[i].a)
                count++,end=a[i].b;
        cout<<count;
    return 0;
}

这里用到了运算符重载,不懂的戳戳这儿——>>>运算符重载便宜卖包学包会

其实这份代码和我思考的方向是不同的。
我是计算拿掉了多少根,他是直接计算保留了多少根,count记录了保留的数目,定义的时候你就知道它最少保留一根吧!
我从 a[0] 开始储存元素,当然,前面有过一个升序的捆绑排序,是以右端点为准的捆绑排序。不知道什么是捆绑排序的人呵呵——>>>捆绑排序真的很重要你必须要戳进来
一轮排序完毕以后,我们看这个循环。
end一开始指向第一条线段的最左,那么你要判断的是最近的那一条——取或者不取。取或不取其实就是看有没有交,有交那就不取。
因此,这个条件 if(end<=a[i].a) 其实就是判断end也就是前一条线段的最右,和后一条线段的最左,有没有交点,没有就count++,end=a[i].b; 而且调整end到你选择的这一条线段的屁股。
最后,输出答案。

事实上,这就是一种不那么直观的贪心算法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值