P3028 [USACO10OCT]汽水机Soda Machine

P3028 [USACO10OCT]汽水机Soda Machine

题意翻译
为了满足fj所有的N(1<=n<=50000)头奶牛的需求,fj新买了一台汽水机。他想找到一个最完美的位置来安放它。

奶牛的牧场可以被表示为一个一维数轴,第i个奶牛被放牧的区间是[Ai…Bi](包含端点),fj可以把汽水机放在[1…1,000,000,000]。

因为奶牛们都懒得要死,她们想尽可能的少移动。她们希望汽水机被放在自己的放牧区间内。

遗憾的是,fj并不总能满足所有奶牛的要求,所以他想请你帮忙算出他能满足的奶牛数目

输入输出样例
输入 #1
4
3 5
4 8
1 2
5 10
输出 #1
3

记录我第一次写博客和第一次在洛谷在做题(小菜鸡一枚)

当看到这道题时,我第一想法是暴力求解,遍历最小到最大的位置区间 i,然后取得最大值:

#include<iostream>
#include<stdio.h>
using namespace std;
struct Node{
	int max,min;
};
int main()
{
	int n;
	int MAX = 0;
	int MIN = 1000000000;
	int ans = 0;
	Node node[50005];
	cin>>n;
	for(int i = 0;i < n;i++)
	{
		scanf("%d",&node[i].min);
		scanf("%d",&node[i].max);
		if(node[i].max > MAX)
		MAX = node[i].max;
		if(node[i].min < MIN)
		MIN = node[i].min;
	}
	for(int i = MIN;i <= MAX;i++)
	{
		int num = 0;
		for(int j = 0;j < n;j++)
		{
			if(i >= node[j].min && i <= node[j].max)
			{
				num++;
			}
		}
		if(num > ans)
		ans = num;
		if(num == n)
		break;
	}
	cout<<ans;
} 

很显然,由于题目所给的数值过大,几乎一半数据超时了,只有54分。

怎么办呢,我们观察可以发现,母牛的分布的位置区间有个跨度(例如母牛a在3~10086之间活动)其中的间隙有多大,对于我们解题没有帮助,所以我们可以尝试将位置区间换个数字,缩小间距,即离散化

举个例子:
母牛a(3,100) 母牛b(7,50) 母牛c(50,100)
题目中只有四个有用数据:3,7,50,100
我们将等效其转变为1,2,3,4
那么母牛a(1,4) 母牛b(2,3)母牛c(3,4)
然后设一个num[100000],计算对每个单独地点有多少母牛能到达,取最大值,就能省去很多遍历

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
struct Node{
	int x,i,z;
	bool operator<(const Node &k){
		return x < k.x;
	}
};
int main()
{
	int n;
	int ans = 0;
	Node node[100010];
	int num[100010];
	int sum[100010];
	cin>>n;
	for(int i = 0;i < n;i++)
	{
		scanf("%d",&node[2 * i].x);
		scanf("%d",&node[2 * i + 1].x);
		node[2 * i].i = 2 * i;
		node[2 * i + 1].i = 2 * i + 1;
	}
	sort(node,node + 2 * n);
	node[0].z = 1;
	for(int i = 1;i < 2 * n;i++)
	{
		if(node[i].x == node[i-1].x)
		node[i].z = node[i-1].z;
		else
		node[i].z = node[i-1].z + 1;
	}
	for(int i = 0;i < 2 * n;i++)
	{
		num[node[i].i] = node[i].z;
	}
	for(int i = 0;i < n;i++)
	{
		//cout<<num[2 * i]<<' '<<num[2 * i + 1]<<endl;
		for(int j = num[2 * i];j <= num[2 * i + 1];j++)
		{
			sum[j]++;
			if(sum[j] > ans)
			ans = sum[j];
		}
	}
	cout<<ans;
} 

然而还是没有满分,只有72分。

代码在最后的 计算每个格子能存在的最大母牛 上能做优化。
引入差分数组的概念
定义:
对于已知有n个元素的数列d,建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。

计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,d[i]=f[i]的前缀和。
快速处理区间加减操作:
对数列区间[L,R]中的每个数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;

最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。

这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;
(摘自https://www.cnblogs.com/young-children/p/11749156.html.)

简单的来说,我们原本应该对num数组中3~8号元素+1,然后遍历num数组取最大值,num记录的就是有多少母牛能到的数目。现在num改为记录与上一位元素的差值num[ i ] - num[ i - 1] ,i 点的母牛数就是 ∑ k = 1 n n u m [ k ] \displaystyle \sum_{k=1}^n num[ k ] k=1nnum[k] ,对3~8号元素+1就是num[ 3 ]++,num[ 9 ] - -。

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
struct Node{
	int x,i,z;
	bool operator<(const Node &k){
		return x < k.x;
	}
};
int main()
{
	int n;
	int ans = 0;
	Node node[100010];
	int num[100010];
	int sum[100010];
	cin>>n;
	for(int i = 0;i < n;i++)
	{
		scanf("%d",&node[2 * i].x);
		scanf("%d",&node[2 * i + 1].x);
		node[2 * i].i = 2 * i;
		node[2 * i + 1].i = 2 * i + 1;
	}
	sort(node,node + 2 * n);
	node[0].z = 1;
	for(int i = 1;i < 2 * n;i++)
	{
		if(node[i].x == node[i-1].x)
		node[i].z = node[i-1].z;
		else
		node[i].z = node[i-1].z + 1;
	}
	for(int i = 0;i < 2 * n;i++)
	{
		num[node[i].i] = node[i].z;
	}
	for(int i = 0;i < n;i++)
	{
		//cout<<num[2 * i]<<' '<<num[2 * i + 1]<<endl;
		/*for(int j = num[2 * i];j <= num[2 * i + 1];j++)
		{
			sum[j]++;
			if(sum[j] > ans)
			ans = sum[j];
		}*/
		sum[num[2 * i]]++;
		sum[num[2 * i + 1] + 1]--;
	}
	int s = 0;
	for(int i = 1;i <= 2 * n;i++)
	{
		s += sum[i];
		if(s > ans)
		ans = s;
		//cout<<s<<' '<<sum[i]<<endl;
	}
	cout<<ans;
} 

这样就有100分了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值