[usaco2010 Oct]Soda Machine (入门oj Problem 6195 )

鬼知道我有多蒟蒻,第五次发题解了

题目原地址点此进入

呜呜呜~今天在CSDN上看了好多大佬神犇的OI退役文,百感交集,故写篇题解振奋自己。

题目描述

To meet the ever-growing demands of his N (1 <= N <= 50,000) cows,
Farmer John has bought them a new soda machine. He wants to figure
out the perfect place to install the machine.

The field in which the cows graze can be represented as a one-dimensional
number line. Cow i grazes in the range A_i…B_i (1 <= A_i <= B_i;
A_i <= B_i <= 1,000,000,000) (a range that includes its endpoints),
and FJ can place the soda machine at any integer point in the range
1…1,000,000,000. Since cows are extremely lazy and try to move
as little as possible, each cow would like to have the soda machine
installed within her grazing range.

Sadly, it is not always possible to satisfy every cow’s desires.
Thus FJ would like to know the largest number of cows that can be
satisfied.

To demonstrate the issue, consider four cows with grazing ranges
3…5, 4…8, 1…2, and 5…10; below is a schematic of their grazing
ranges:

     1   2   3   4   5   6   7   8   9  10  11  12  13

     |---|---|---|---|---|---|---|---|---|---|---|---|-...
     	     aaaaaaaaa
     	         bbbbbbbbbbbbbbbbb
     ccccc           ddddddddddddddddddddd

Sample Output

As can be seen, the first, second and fourth cows share the point 5,
but the third cow’s grazing range is disjoint. Thus, a maximum of
3 cows can have the soda machine within their grazing range.
有N个人要去膜拜JZ,他们不知道JZ会出现在哪里,因此每个人有一个活动范围,只要JZ出现在这个范围内就能被膜拜,
伟大的JZ当然希望膜拜他的人越多越好,但是JZ不能分身,因此只能选择一个位置出现,他最多可以被多少人膜拜呢,
这个简单的问题JZ当然交给你了

输入

  • Line 1: A single integer: N
  • Lines 2…N+1: Line i+1 contains two space-separated integers: A_i and B_i

输出

  • Line 1: A single integer representing the largest number of cows
    whose grazing intervals can all contain the soda machine.

样例输入

4
3 5
4 8
1 2
5 10

样例输出3

OUTPUT DETAILS:
If the soda machine is placed at location 5, cows 1, 2, and 4 can be
satisfied. It is impossible to satisfy all four cows.

题目大意

其实这道题目意思很简单啦,就是给你一个极长的一根线段,然后选取n组点[x,y]并在此区间染色,求染色次数最多是多少?
本题推荐按此流程做:
校门外的树
光线
Soda Machine

暴力

其实这题暴力很简单的,直接开一个数组在[x,y]这段区域内权值+1就行,但是很显然,虽然染色次数n不多只有50000,但因为染色区域过长会造成极其严重的超时!

击破TLE!

刚刚我们说到,这个题主要超时的原因是因为染色区域过长每一次染色区域最多可以达到近十亿个单位长度,所以我们应克服的问题就是这里,如何优化每一次染色呢?这就需要引进差分思想

这个题其实你有感觉是要用前缀和的,但是似乎没什么屁用,其实不然,事实上染色的本质就是将[x,y](x<=y)
这一段凸显出来,使它看起来更特别,为了“看上去”是“特别”的,我们除了将它们全部打上标记,也可以在染色的两个端点打上标记(因为染色段是连续的,只要确定两端点那么这种染色方式就被唯一确定),即x和y;

但是这一题我们打标记的方式有所不同,我们应该开一个整形变量的数组而不是逻辑数组用于记录区域之间被染色的次数,假设这个数组为k,我们要给k[x]——k[y]之间的区域染色,事实上只要将k[x]+1,将k[y+1]-1,再求一遍前缀和,那么k[x]——k[y]就被全部加了(对于初始状态数组中全赋值为0时,该操作将[x,y]之间全赋值为1)。

一样的,对于样例数据来说。我们可以画出如下表格:
在这里插入图片描述

然后求一遍前缀和。
在这里插入图片描述
可以看出在哪个位置被染了几次,并且最大值为3.
伪代码如下:

for(int i=1;i<=n;i++)scanf("%d%d",&a,&b),ch[a]++;ch[b+1]--;
fh[1]=ch[1];
for(int i=2;i<=n;i++)
{
	fh[i]=fh[i-1]+ch[i];
	if(fh[i]>=1)ans++;
}
cout<<ans<<endl;//可以加一个快读

然而肯定会爆内存(fh[1,000,000,000],最多只能开33,000,000左右)

击破MLE!

综上所述,这一题实在是忒恶心,不仅需要用到差分,还要用离散化!(虽然比这恶心的题目多了去了)

仔细阅读题面,我们可以发现染色的两个端点的数值实际意义并不大,重要的是他们的相对意义,就比方说1,200,3000,4000,99999999,232-1这一组数据,在本题来看,其实和1,2,3,4,5没有太大的区别,所以我们可以对过大的数据进行转化,在不改变相对大小的情况下改变其本身大小,而这恰好就是离散化操作!

离散化的大致思想是:先将数据排序,然后将排完序后的数组a[i]赋值为i,再将这个值放回原先未排序数组的位置处
流程:6 8 9 4

  1. 4 6 8 9(原位置:4 1 2 3)
  2. 1 2 3 4(原位置:4 1 2 3)
  3. 2 3 4 1 (将1放到第4位,将2放到第1位,将3放到第2位,将4放到第3位,就有如下结果)

所以代码应该这么写:

struct wssb{int x,idx;}a[200001];//x表示权值,idx表示位置
bool cmp(wssb a,wssb b){return a.x<b.x;}//cmp函数
int lsh[200001];//离散化结果
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
    scanf("%d",&a[i].x);
    a[i].idx=i;//记下其位置
}
sort(a+1,a+n+1,cmp);//把数据按照其权值排序,相当于上述第一个步骤。
for(int i=1;i<=n;++i)lsh[a[i].idx]=i;//注:这里将上述的第二个步骤和第三个步骤合并了

终于克服了这两大难题,成功AC!

AC代码

//注:AC代码会根据题面而相对于前面的伪代码有所调整,前面讲的只是模版代码。
#include <bits/stdc++.h>
using namespace std;
int n,cnt,ans,x1,x2;
int lsh[200001],k[200010];
struct wssb{int x,idx;}a[200001];
bool cmp(wssb a,wssb b){return a.x<b.x;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x1,&x2);
		a[++cnt].x=x1;a[++cnt].x=x2;
		a[cnt-1].idx=cnt-1;a[cnt].idx=cnt;
	}
	sort(a+1,a+cnt+1,cmp);
	for(int i=1;i<=cnt;i++)
	{
		lsh[a[i].idx]=i;
		if(a[i].x==a[i-1].x)lsh[a[i].idx]--;
	}
	for(int i=1;i<=n;i++)k[lsh[2*i-1]]++,k[lsh[2*i]+1]--;
	for(int i=1;i<=2*n+1;i++)k[i]=k[i]+k[i-1],ans=max(ans,k[i]);
	printf("%d\n",ans);
	return 0;
}

希望得到神犇的指教。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值