noip 2011 day1 t2 选择客栈

#     暴力出奇迹!!!!!

   第二次做这个题 感受颇深
     
   何出此言,对于我好久以前做此题,没有任何思路,除了30的暴力不加剪枝,
       然后我就看了博主一的题解,感觉很懂的样子,
  时至今日,忘光光了,然后就有了暴力出奇迹!


 进入正题


  对于本题,一开始想到了两种方法。
   第一种,暴力枚举同样色调的客栈,然后枚举其中的满足最低消费的客栈,显然会TLE。而且死的很惨!!代码就很简单了
      第二种,暴力枚举满足最低消费的客栈,然后预处理每个客栈每种两边客栈颜色的数量,然后ans直接加左右相乘就ok,然而码到一半的时候,果断放弃,为什么? 两个客栈之间会有多个满足最低消费的客栈。
    正解
     结合一下一二种方法(也不算是结合,优化一下第一种的做法)。我们枚举一个客栈,然后找到在它之后的一个满足最低消费的客栈。预处理此满足最低消费客栈之后(包括此客栈)和枚举的那个客栈的颜色相同的客栈。(细细体会,不难理解)
 上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long 
using namespace std;
const int N=200050,K=55,P=150;
int n,k,p,col[N],w[N],r[N][K],wz[N],num=0,fl=0;
ll ans=0;
int find(int x){//二分查找离x客栈最近的满足最低消费的客栈,个人觉得二分会快点 
	int a=1,b=num,ddd=-999;
	while(a<=b){
		int mid=(a+b)/2;
		if(wz[mid]>=x){
			ddd=wz[mid];
			b=mid-1;
		}
		else a=mid+1;
	}
	return ddd;
}
int main(){
	scanf("%d%d%d",&n,&k,&p);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&col[i],&w[i]);
		if(w[i]<=p){
			num++;
			wz[num]=i;//统计满足最低消费的位置 
		}
	}
	for(int i=n;i>=1;i--){
	  for(int j=0;j<k;j++)
	   if(col[i]==j)
	     r[i][j]=r[i+1][j]+1;
	    else
		 r[i][j]=r[i+1][j]; 
	} //预处理每个节点之后的各种色调的客栈数量 
	for(int i=1;i<=n;i++){
	 	if(fl<i) fl=find(i);//只有当前的在最近的满足最低消费的客栈之后,再更新最近客栈,毕竟可以节约时间 
	 	if(fl!=-999){//之后没有满足的客栈了,直接跳出循环 
	 		if(fl==i) //这个地方是个坑点,因为两个人不能住同一个客栈
	 		  ans+=r[fl][col[i]]-1;//因为我们统计的时候算上了本身,所以要减去一
	            else ans+=r[fl][col[i]]; 
	 	}
	 	else break;
	 }
	printf("%d",ans);
	return 0;
}

然而,这题有更优的做法(此话怎讲?)

O(N),直接线性推。用三个数组维护,sum[col],cnt[col],last[col]。也就算是上面代码的更优化。

    sum[col]把统计后面的满足最低消费客栈的数量(K*N)改为统计前面的(后面也一样)。 

   cnt[col]记录前面的col颜色的数量。

   last[col]相当于二分枚举合法位置。。。。。。。。。。。。。。。。。。。。。。。

(题解就是不一样(补营养),orz orz)

废话少说,上代码!

#include<iostream>
#include<cstdio>
using namespace std;
const int K=55;
int n,k,p,cnt[K],last[K],now,ans,sum[K];
int main(){
	scanf("%d%d%d",&n,&k,&p);//输入数据
	for(int i=1;i<=n;i++){
		int col,w;
		scanf("%d%d",&col,&w);
		if(w<=p) now=i;//更新合法位置
		if(last[col]<=now) sum[col]=cnt[col];//求满足要求的客栈数
		last[col]=i;//更新上一个col的位置
		ans+=sum[col];//更新答案
		cnt[col]++;//col数量增加
	}
	printf("%d",ans);//输出答案
	return 0;
} 

本题在于优化的思路,不在于题的难易。思路可以应用在各大难题上。(该暴力的还是暴力,想不出正解orz)

谨记此博文记于2019,3,21(orz,orz)

猜你喜欢

转载自blog.csdn.net/qq_42198700/article/details/88658204