选择客栈 noip2011

在openjudge上做的题目,原来是2011年的=====。


题目:


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

两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p元的咖啡店小聚。


 2<=n<=200000, 0<k<=50, 0<=p<=100, 0<=最低消费<=100


也就是找出所有对色调相同的旅馆并且中间的最小费用小于p


首先想到的是 O(n^2)的,想到很容易,但20,000肯定爆炸。

然后想到的是拿一个数组保存相同色调的,可能好一点,万一,万一,万一最坏颜色全部一样(N^2)那就爆炸了。


其实这道题能用滚动数组优化,注意下面这点:

       如果 第i+1个色调为k 的客栈 能访问第i个色调为k的客栈,那么对于以后的所有色调为k的客栈都可以访问第i个色调为k的客栈。

      简单的说,如果这个客栈能被访问,那么后面所有的同类客栈都可以访问他,毕竟后面到该客栈的费用必定 <=price[i] <= p。


我们只要2个数组

    一个用来保存可以直接访问的。 

    一个用来保存无法访问的;


如果发现连通,那么 可以直接访问的个数 += 无法访问的个数(根据上面的推理,这些客栈都全部通了)


//其实一个也可以,只需要记录色调k客栈出现的次数,然后每次 访问个数 = 出现个数。


另外每个点每个颜色更新一下最短值。

这样在O(nk)就解决了。

#include <iostream>
#include <cstring>
#define inf 0x3f3f3f3f
using namespace std;

int n,k,p;
int direct[51];
int block[51];
int pre[51];


int main()
{
	memset(pre,0x3f,sizeof(pre));
	int ans = 0;
	cin >> n >> k >> p;
	int x = 0 , y;
	
	for (int i = 1 ; i <= n ; ++i)
	{
		cin >> x >> y;   
		if(pre[x] == inf)    //如果路径无穷大 保存
		{                     //加上必通的 
		  block[x] = 1;      //累计数量=1 
		  pre[x] = y;        //最低费用为y 
		  
		  for (int j = 0;j < k ;++j)
		    if (pre[j] != inf)
		      pre[j] = min (pre[j],y);  //如果费用存在,则更新最小费用 
		      
		  continue;          //继续循环 
		 } 
		  
		  
		for (int j = 0;j < k ;++j)
		    if (pre[j] != inf)
		      pre[j] = min (pre[j],y);  //如果费用存在,则更新最小费用 
		    
			if(pre[x] <= p) //如果费用满足要求,那么对于后面的客栈,前面的路全通 
			 {
				direct[x] += block[x];
				block[x] = 1;  //不满足数量为 0 
				pre[x] = y;  //最小费用为当前客栈 
			}
			else           //如果大于,则卡住 +1 
				block[x]++;
	    ans += direct[x];     //加上直接可以取的
	   
	}
    cout << ans << endl;
}



补:后来改了一下,改成一个数组,两个数组真的啰嗦,直接设个计数器就简单多了,还调整了下代码,主代码大概就20行左右!


#include <iostream>
#include <cstring>
#define inf 0x3f3f3f3f
using namespace std;

int n,k,p;
int direct[51];
int cnt[51];
int pre[51];

int main()
{
	memset(pre,0x3f,sizeof(pre));
	int ans = 0;
	cin >> n >> k >> p;
	int x = 0 , y;
	for (int i = 1 ; i <= n ; ++i)
	{
		cin >> x >> y;   
		cnt[x]++;
		if(pre[x] == inf) pre[x] = y;
		 for (int j = 0;j < k ;++j)
		    if (pre[j] != inf)
		      pre[j] = min (pre[j],y);  //如果费用存在,则更新最小费用 
		      
			if(pre[x] <= p) //如果费用满足要求,那么对于后面的客栈,前面的路全通 
			 {
				direct[x] = cnt[x] - 1;
				pre[x] = y;  //最小费用为当前客栈 
			 }
				
	    ans += direct[x];     //加上直接可以取的
	}
    cout << ans << endl;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值