洛谷 NOIP2018普及组 龙虎斗 + 洛谷 CSJ-2019 公交换乘 代码优化

这一道题目的题干比较长,然后我第一个方法是没过的,无脑的暴力枚举果然不具有普适性,不过也能达到80分,也还算是比较可观了,然后我优化了一下,把O(n^2)的时间复杂度优化成了O(n),接下来读者请看题目:

然后是输入输出:

最后是样例说明:

 然后是数据规模:

题干的长度真的很长,不过好在难度并不是很大,接下来话不多说,直接看第一种方法暴力枚举,上代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,p1;
long long c[110000],s1,s2;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>c[i];
	cin>>m>>p1>>s1>>s2;
	long long ans=-1;
	int ansp2=-1;
	for(int p2=1;p2<=n;p2++){
		c[p1]+=s1;
		c[p2]+=s2;
		long long sum1=0,sum2=0;
		for(int i=1;i<=n;i++){
			if(i<m) sum1+=c[i]*(m-i);
			else if(i>m) sum2+=c[i]*(i-m);
		}
		long long tmp=abs(sum1-sum2);
		if((tmp<ans) || ansp2==-1){
			ans=tmp;
			ansp2=p2;
		}
		c[p1]-=s1;
		c[p2]-=s2;
	}
	cout<<ansp2<<endl;
	return 0;
}

 属于是一点脑子都不动这个方法,就是在输入完之后,我们引入一个ansp2,这个变量是来第一次更新ansp2的时候用到的,然后在每一次枚举之中,通过套两重循环,来不断的通过tmp和当时的ans进行比较,来判断是否需要更新ans还有ansp2,sum1,sum2分别是两个阵营的气势之和,最后输出我们要的ansp2即可。

接下来请看优化之后的代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,p1;
long long c[110000],s1,s2;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>c[i];
	cin>>m>>p1>>s1>>s2;
    c[p1]+=s1;
    long long sum1=0,sum2=0;
	for(int i=1;i<=n;i++){
		if(i<m) sum1+=(m-i)*c[i];
		else if(i>m) sum2+=(i-m)*c[i];
	}
	long long ans=-1;
	int ansp2=-1;
	for(int p2=1;p2<=n;p2++){
		long long t1=sum1,t2=sum2;
		if(p2<m) t1+=s2*(m-p2);
		else if(p2>m) t2+=s2*(p2-m);
		long long tmp=abs(t1-t2);
		if(ansp2==-1 || tmp<ans){
			ans=tmp;
			ansp2=p2;
		}
	}
	cout<<ansp2<<endl;
	return 0;
}

第二种方法其实思路差距也并不大只不过经过操作减少了一重循环,第二种方法就是在外先计算出sum1和sum2的答案,然后从1到n进行枚举每次判断在相应的地方如果加上s2的兵力,能不能让气势差更小,另外题目要求当有多种方式满足条件的时候,优先输入较小的方案,所以我们在循环之中的条件是tmp<ans,考虑到第一次更新ans和ansp2,我们对ans赋初始值-1。

这道题目虽然题量比较大不过也并不难理解题意只是数据多然后插图比较多所以导致读者阅读量较大,不过就算不考虑到时间复杂度的优化一样可以得到80的分数,而且对思维的要求不怎么高。

看第二道题目:公交换乘

这一道题目我感觉还是很难的,难在时间复杂度上面,实际上这道题目给我的第一印象就是模拟题,只不过确实感觉对思路还有时间复杂度都是有一定要求的,我第一个代码就是时间复杂度没过,接下来上题目:

 

首先给出我一开始超时的代码:

#include<iostream>
using namespace std;
int n;
int ans;
int k=1;
int money[100100],t[100010]; 
int main(){
     	cin>>n;
     	for(int i=1;i<=n;i++){
     		int way,price,time;
     		cin>>way>>price>>time;
     		if(way==0){
			money[k]=price;
			t[k]=time;
			++k;
			ans+=price;
     	    }
     	    else {
     	    	bool pd=false;
			for(int j=1;j<=k;j++){
				if(money[j]>=price && time-t[j]<=45)
				{
				pd=true;
				money[j]=0;
				break;
				}
			}
			if(pd==false)
			ans+=price;  
			}
		 }
		 cout<<ans<<endl;
		 return 0;
}

 主要还是循环的时候,当我用掉了优惠票之后,没有用一种方法把下一次循环的可能性减去一,s顺带一提,上述代码如果把cin改成scanf的话可以多得10分,然后我根据问题,想到了借助使用结构体来解决,接下来我给出正确的代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
struct ticket{
	int time,price;
	bool used;
}a[100010];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int way,p,t;
		cin>>way>>p>>t;
		if(way==0){
			ans+=p;
			m++;
			a[m].time=t;
			a[m].price=p;
			a[m].used=false;
		} else {
			int id=-1;
			for(int j=m;j>=1;j--){
				if(a[j].time<t-45) break;
				if(a[j].price>=p && !a[j].used)
				id=j;
			}
			if(id!=-1)
			a[id].used=true;
			else ans+=p;
		}  
	}
	cout<<ans<<endl;
	return 0;
}

我们定义的结构体数组用来存储所有的优惠票信息,其中的三个变量包含了优惠票的价格上限,以及得到优惠票的时间,还有bool变量对应的优惠票的使用情况,当我们每次乘坐地铁的时候,就会增加一张优惠票,然后false代表优惠票未被使用,然后当way==1的时候就令一个中间变量id,如果找到了可以使用的优惠票就直接令id为对应的票的编号j,然后把他设置为已经使用过。

公交换乘在模拟题里面算是比较难的了,希望这篇博客对读者有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

残念亦需沉淀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值