深信服8月25日笔试的两道编程题

第一题比较难,我是在算小样例中总结出的规律,第二题比较简单。好在都AC了

第一题:

题目描述:

给你N棵树,每棵树都有一个高度a[i],  (N和a[i]都是一百万数量级)
你每次可以施展魔法让其中一棵树不生长而其它树高度都加一,
问最少几次魔法可以使得所有树一样高。

分析:

对于次高的树来说,想要达到最高的高度,我们只需要施展【他们高度差值】的次数魔法。

例如两棵树的高度是6和4,那么只需要2次让6不动,4就可以变成6.

当然这只是对于最高的树只有一棵来说的,若是有m棵最高的树,则次高树想变成最高树需要施展【m*高度差值】次数的魔法。 

例如三棵树6 6 4,想变成相等高度的树,需要2*2=4次,具体过程是【6 7 5】 【7 7 6】 【7 8 7】【8 8 8】

当然这里次高树的棵树是没有影响的。

例如次高树也有两颗,但是答案不变,假如4棵树6 6 4 4,具体过程【6 7 5 5】【7 7 6 6】【8 7 7 7】【8 8 8 8】

有了把次高树变成最高树的策略,那所有问题都解决了,因为次高树变成了最高树,那么再考虑次次高树,次次次高树即可,所有的都可以由这一个策略算出来。

这里需要注意的是,魔法只会针对最高树使用,因为遏制其他的树生长只会让差值越来越大,与我们的目的相反。

解法:

先把N棵树按高度排序,并且去重,统计每个高度树的棵树。

在解决最高树和次高树的过程中,我们要维护以下几个关键的有用的量:

一、次高树与最高树的高度差: int cha=maxx-(b[i]+ans);

随着越来越多的树变成了最高树的高度,次高树的高度也在随着变化,他们变成了【本身高度+魔法次数】的高度。

二、使用魔法的次数    ans+=cha*m;

如上述分析,次高树想变成最高树需要施展【m*高度差值】次数的魔法。 

三、最高树的高度   maxx+=cha*(m-1);

最高树的高度变化与普通树不同,对于除了最高树之外的普通树来说,魔法施展几次,他们就会增加几,因为他们从不被遏制生长。

但是最高树会被魔法遏制,所以在次高树变成最高树的时候,最高树的高度只是增加了【差值次数*(最高树棵树-1)】的高度。

这里可以参照分析的例子【6 6 4 4】,经过4次变化,达到了【8 8 8 8】的高度,两棵高度为4的树生长高度对应魔法次数4,但是最高树因为被遏制只生长了2,也就是【差值次数*(最高树棵树-1)】的高度。

四、最高树的棵树   m+=c[i];    

次高树变成了最高树之后,要统计入答案。

维护了这4个量,我们就可以O(n)时间求出答案。最重要的是理解最高树和普通的树在高度变化过程中的数量差异。

代码:

#include<iostream>
#include<cstdio> 
#include<algorithm>
using namespace std;

int a[1000050];
int b[1000050];
int c[1000050];
int n;

bool cmp(int x,int y){
	return x>y;
}

int main(){
	cin>>n;
	for (int i=1; i<=n; i++){
		scanf("%d",&a[i]);
	} 
	//排序去重 
	sort(a+1,a+n+1,cmp);
	b[1]=a[1];
	c[1]=1;
	int p=1;
	for (int i=2; i<=n; i++){
		if (a[i]!=b[p]){
			p++;
			b[p]=a[i];
			c[p]=1;
		} else c[p]++;
	}

	int maxx=b[1]; //最大值 
	int m=c[1];  //最大值个数 
	long long ans=0;
	for (int i=2; i<=p; i++){
		int cha=maxx-(b[i]+ans);
		ans+=cha*m;
		maxx+=cha*(m-1);
		m+=c[i];	
	}
	cout<<ans<<endl; 
} 

第二题:

题目:

给你一个长度一百万的字符串s,全是由0-9的数字构成,
再给你一百万次的修改操作,每次操作两个数x,y,表示把字符串s中所有的x都换成y。
最后输出字符串s。

分析:

一百万次的修改操作,其中无用的或者重复的很多,因为10*10最多才100种组合,其中不乏很多把2变成4,再把2变成6的操作,或者把2变成4,把4变成6的操作,所以不要在没有最终情况下去动字符串,而是先处理好这个规则。

解法:

开一个大小为10数组a,下标表示字符串中的原数,内容a[i]则表示最终将变成的值。

对于一个修改操作x,y来说,需要在数组找所有的a[i]==x,若有需要执行a[i]=x.

代码就是: for (int i=0; i<=9; i++) if (a[i]==x) a[i]=y; 

假设修改操作是2->4  4->6,

    在a数组中原来是i和a[i],一一对应的,修改2->4时,找到的a[2]=x,然后执行a[2]=4.

    当修改4->6时,for循环中a[2]和a[4]都等于x,所以得修改a[2]=6 a[4]=6.

重点是:一个数只能变成另一个数字,但是一个数字的来源是不唯一的,所以我们要将x变成y的话需要遍历一遍数组去检查所有的a[i]有哪些原始的i变成了x,这些x都需要修改。

最后变换字符串即可。

代码:

#include<iostream>
#include<cstring>
#include<cstdio> 
using namespace std;

char s[1000050];
int a[100];
int n,x,y;

int main(){
	for (int i=0; i<=9; i++){
		a[i]=i;
	} 
	scanf("%s",s);
	int len=strlen(s);
	cin>>n;
	for (int o=1; o<=n; o++){
		scanf("%d%d",&x,&y);
		for (int i=0; i<=9; i++){
			if (a[i]==x) a[i]=y; 
		}
	} 
		
	for (int j=0; j<len; j++){
		s[j]=a[(s[j]-48)]+48;
	}
	
	printf("%s",s);
}

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值