洛谷P1311 [NOIP2011 提高组] 选择客栈 题解

题目

题目描述

丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从 1 到 n 编号。每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0∼k−1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。

两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p 。

他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p 元的咖啡店小聚。

输入格式

共 n+1 行。

第一行三个整数 n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;

接下来的 n 行,第 i+1 行两个整数,之间用一个空格隔开,分别表示 i 号客栈的装饰色调 ai​ 和 i 号客栈的咖啡店的最低消费 bi​。

输出格式

一个整数,表示可选的住宿方案的总数。

输入输出样例

输入 #1

5 2 3 
0 5 
1 3 
0 2 
1 4 
1 5 

输出 #1

3

说明/提示

样例解释

2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,但是若选择住 4,5号客栈的话,4,5 号客栈之间的咖啡店的最低消费是 4 ,而两人能承受的最低消费是 3 元,所以不满足要求。因此只有前 3 种方案可选。

数据范围

对于 30% 的数据,有 n≤100 ;

对于 50% 的数据,有 n≤1000;

对于 100% 的数据,有 2≤n≤2×10^5,1≤k≤50,0≤p≤100,0≤bi​≤100。

分析

这道题的50% 的数据 n≤1000,所以用暴力方法就可以拿到50+,最高能拿到70分(当然,由于这道题的测试点有点水,所以对暴力程序进行剪枝也能拿到100分)

这道题的满分做法数据范围很大,得出的答案也很大,所以用来存储方案数的变量都得定义成long long 类型

暴力代码(70分)

#include <bits/stdc++.h>
using namespace std;
int n,k,p,a[200005],b[200005],g[200005],cnt;
long long ans;
int main()
{
 	int i,j,t;
 	scanf("%d%d%d",&n,&k,&p);
 	for(i=1;i<=n;i++)
 	{
 		scanf("%d%d",&a[i],&b[i]);
 		if(b[i]<=p) g[++cnt]=i;
	}
	for(i=1;i<=n;i++)
	{
		for(j=i+1;j<=n;j++)
		{
			if(a[i]==a[j])
			{
				for(t=i;t<=j;t++)
				{
					if(b[t]<=p)
					{
						ans++;break;
					}
				}
			}
		}
	}
	cout<<ans;
 	return 0;
}

关于这个代码的讲解就不用多说了吧

满分代码讲解

1.变量定义

int n,k,p,a[200005],b[200005];
long long sum[105],res;

第一行的变量含义和题目中的相同,第二行的sum[i]代表选择i号色调的方案数,res代表最终方案数

2.输入

scanf("%d%d%d",&n,&k,&p);
for(i=1;i<=n;i++)
{
 	scanf("%d%d",&a[i],&b[i]);
}

这里就不用多说了吧

3.得出方案数(程序中最重要的部分)

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

如果当前客栈的最低消费小于等于p,则把之前每一个客栈所对应的色调的方案数加1,最终答案就加上该客栈色调的方案数减去它自己。注意加过的就不要重复加了,即j就不要重新归零了,但j刚开始的初值要等于0;

如果当前客栈的最低消费大于p,则最终答案就加上该客栈色调的方案数,注意这里就不要减去它自己了

最后再输出最终答案res,整个程序也就结束了

完整AC代码

#include <bits/stdc++.h>
using namespace std;
int n,k,p,a[200005],b[200005];
long long sum[105],res;
int main()
{
 	int i,j=0;
 	scanf("%d%d%d",&n,&k,&p);
 	for(i=1;i<=n;i++)
 	{
 		scanf("%d%d",&a[i],&b[i]);
	}
	for(i=1;i<=n;i++)
	{
		if(b[i]>p) res+=sum[a[i]];
		else
		{
			while(j<i)
			{
				j++;
				sum[a[j]]++;
			}
			res+=sum[a[i]]-1;
		}
	}
	cout<<res;
 	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值