清华 2011 剩下的树

题目:

长为整数L(1<=L<=10000)的马路,在每个整数坐标点有一棵树,即在0,1,2...LL+1个位置上有L+1棵树。要移走某区间内的树,如 100 200表示移走从100200之间(包括端点)的树。有M(1<=M<=100)个区间,区间之间可能重叠。求剩下的树的个数。

 

思路:

离散的区间合并问题,可以用标记法:即把域内的整数都初始标记为0,有区间括起来的整数标为1,则剩下的即为没在区间内的。


#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

int L,M,n;
int A[10002];

int main(){
	int a,b;
	int i,j;
	while(scanf("%d %d",&L,&M)!=EOF){   //输入L,M
		n=0;
		memset(A,0,10002*sizeof(int));      //初始化标记为0
		for(i=0;i<M;i++){                   //输入区间
			scanf("%d %d",&a,&b);
			for(j=a;j<=b;j++)                     //把该区间的标记都改为1
				A[j]=1;
		}
		for(i=0;i<=L;i++){                  //计算剩下的树(即标记为0)
			if(A[i]==0)
				n++;
		}
		printf("%d\n",n);                   //输出结果      
	}
	return 0;
}

刚开始做复杂了,用数组模拟出区间,然后再一一输入区间,有交集的区间合并。最后再算区间外的数。从代码长度即可知复杂得多(另:该方法在九度上刷不过,但在牛客上没问题,很是纳闷)。

#include <iostream>
#include <stdio.h>
using namespace std;

int L,M,n;
int A[102],B[102];
//处理新区间与原区间
void doInterval(int a,int b);
//判断一个整数在区间集中的位置
bool isIn(int a,int &ia);
//把j开始的区间,都往前挪k位
void moveInternal(int j,int k);

int main(){
	int a,b;
	int i;
	while(scanf("%d %d",&L,&M)!=EOF){   //输入L,M
		n=0;
		if((L<1 || L>10000)||(M<1 || M>100))
			continue;
		for(i=0;i<M;i++){                   //输入区间,归并
			scanf("%d %d",&a,&b);
			if(a>b){
				a=a^b; b=a^b; a=a^b;
			}
			doInterval(a,b);
		}
		for(L=L+1,i=0;i<n;i++){             //计算剩下的树
			L=L-(B[i]-A[i]+1);          
		}
		printf("%d\n",L);                   //输出结果      
	}
	return 0;
}

//处理新区间与原区间
void doInterval(int a,int b){
	int i=0;
	int ia,ib;          
	bool ina,inb;
	ina=isIn(a,ia);   //判断a的位置
	inb=isIn(b,ib);   //判断b的位置
	if(ina){          //a在区间内
		if(inb)            //b在区间内
			B[ia]=B[ib];
		else               //b在空档
			B[ia]=b;
		moveInternal(ib+1,ib-ia);
		n=n-(ib-ia);
	}
	else{             //a在空档
		if(inb){           //b在区间内
			A[ia+1]=a;
			B[ia+1]=B[ib];
			moveInternal(ib+1,ib-ia-1);
			n=n-(ib-ia-1);
		}
		else{              //b在空档
			if(ia==ib){         //a,b在同一空档
				moveInternal(ia+1,-1);
				A[ia+1]=a;
				B[ia+1]=b;
				n++;
			}
			else{               //a,b不在同一空档
				A[ia+1]=a;
				B[ia+1]=b;
				moveInternal(ib+1,ib-ia-1);
				n=n-(ib-ia-1);
			}
		}
	}
}

//判断一个整数在区间集中的位置
bool isIn(int a,int &ia){
	bool in=true;
	int i=0;
	while(i<n){
		if(A[i]<=a)
			i++;
		else 
			break;
	}
	                    //a在区间内
	if((i!=0 && i<n && a<=B[i-1])||(i!=0 && i==n && a<=B[n-1])){  
		ia=i-1;              //a在第ia个区间内
	}
	else{               //a在空档
		ia=i-1;              //a在第ia个区间后的空档
		in=false;
	}
	return in;
}

//把j开始的区间,都往前挪k位
void moveInternal(int j,int k){
	int i;
	for(i=j;i<n;i++){
		A[i-k]=A[i];
		B[i-k]=B[i];
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值