P1311 [NOIP2011 提高组] 选择客栈

1. 题目

[NOIP2011 提高组] 选择客栈 - 洛谷

2. 

1)70分:

①思路

very easy 单纯暴力枚举

三重循环,分别枚举第一家客栈 第二家客栈和咖啡店的位置,中间内容判断色调数目是否一样,一样的话看咖啡馆的价钱是否超过p,如果价钱不超过p且色调数目一样,则符合条件,sum++

②核心代码:

	for(x=1;x<=n;x++)//枚举第一家 
	{
		for(y=x+1;y<=n;y++)//第二家 
		{
			if(hotel[x].a ==hotel[y].a )//色调数目是否相同
			{
				for(z=x;z<=y;z++)//咖啡店位置 
				{
					if(hotel[z].b <=p)//咖啡馆价钱 
					{
						sum++;
						break;
					 }				
				}
			} 
		}
	}

2)满分思路

①题目重塑:

我们要在一维数组中选择两个数a,b 并从a~b之间选择一个数Pc

满足· a,b 的色调数目相同

       · Pc<=p 

求满足条件的a b 组数

②思路:

为了降低循环层数,我们思考,是否可以只枚举一个客栈,通过某种计算或者操作得到另一个客栈的个数呢

答案是肯定的:我们发现只要得知一个点b(编号较大的客栈的位置),判断离他最近的咖啡馆的位置,记录这个咖啡馆之前与b色调数目相同的点的个数,一直累加,就能得到答案。

思路解释:我们知道,只要a,b 之间有一个咖啡馆就行,不考虑咖啡馆的位置,即咖啡馆的位置不会影响最后输出的个数答案,所以只要知道离点b最近的咖啡馆之前与b色调数目相同的客栈的个数,相加即可

但是,为什么是离点b最近的咖啡馆而不是第二近,第三近甚至是最远的呢?

因为根据题目要求,最后输出的方案数一定是最多的(全的,不重不漏),只有当咖啡馆离得最近时,咖啡馆前面所包含的点数更多,那么相应的,枚举到的点a的个数就有可能最多

③核心代码:

	int j=0; 
	for(i=1;i<=n;i++)
	{
		if(hotel[i].b >p) res+=sum[hotel[i].a ];
		else
		{
			while(j<i)
			{
				j++;
				sum[hotel[j].a ]++;
			}
			res+=sum[hotel[i].a ]-1;
		}
	} 

因为在循环中,我首先j++,所以j的初始值为0

这里采用了桶排序的思想,sum[颜色]++

枚举1~n,判断他能否作为咖啡馆,不能的话,直接加 / 离他最近的咖啡馆之前 / 与他颜色相同的数字 / 的个数(句子有点儿长,画个断句)

如果可以的话,更新最近咖啡馆,因为题目中说客栈a b 也可以作为咖啡馆

直接计算出此咖啡馆前所有包含颜色的数字的个数,因为我们不知道后面出现的颜色到底有几种,最后res+=我们枚举到的这个点颜色相同的个数-1

为什么要-1呢?

因为在枚举时,我们的 j 最后的值会等于i,所以在相同颜色的计算中,把当前我们枚举到的这个点加进去了,所以最后的个数要-1

④根据样例,解释代码:

p=3

·i=1: Pc=5>3 无法做咖啡馆,直接加离他最近的咖啡馆前相同颜色的个数 res=0

`i=2:Pc=3<=3 可以做咖啡馆,sum[0]=1,sum[1]=1(他自己); res+=sum[1]-1=0

以此类推~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

·i=3:Pc=2<=3 √,sum[0]=2,sum[1]=1;res+=sum[0]-1=0+(2-1)=1

`i=4:Pc=4>3   × 不更新sum的值 sum[0]=2,sum[1]=1;res+=sum[1]=1+1=2

`i=5:Pc=5>3   × 不更新sum的值 sum[0]=2,sum[1]=1; res+=sum[1]=2+1=3

最后输出res=3

3.完整代码:

70:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,k,p;
struct node
{
	int a,b;
}hotel[N];
int flag[N];
int main()
{
	int i,j,x,y,z,sum=0;
	cin>>n>>k>>p;
	for(i=1;i<=n;i++)
	{
		cin>>hotel[i].a >>hotel[i].b ;
	}
	for(x=1;x<=n;x++)//枚举第一家 
	{
		for(y=x+1;y<=n;y++)
		{
			if(hotel[x].a ==hotel[y].a )//颜色相同
			{
				for(z=x;z<=y;z++)
				{
					if(hotel[z].b <=p)//m
					{
						sum++;
						break;
					 }				
				}
			} 
		}
	}
	cout<<sum;
	return 0;
}

登录 - Luogu Spilopelia

100:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,k,p;
struct node
{
	int a,b;
}hotel[N];
int flag[N];
int sum[N];
int main()
{
	int i,x,y,z,res=0;
	cin>>n>>k>>p;
	for(i=1;i<=n;i++)
	{
		cin>>hotel[i].a >>hotel[i].b ;
	}
	int j=0; 
	for(i=1;i<=n;i++)
	{
		if(hotel[i].b >p) res+=sum[hotel[i].a ];
		else
		{
			while(j<i)
			{
				j++;
				sum[hotel[j].a ]++;
			}
			res+=sum[hotel[i].a ]-1;
		}
	} 
	cout<<res;
	return 0;
}

登录 - Luogu Spilopelia

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值