2021牛客寒假算法基础集训营5 D. 石子游戏(差分/贪心)

链接:https://ac.nowcoder.com/acm/contest/9985/D
来源:牛客网

题目描述

叶妹妹很喜欢玩石头,于是这天泽鸽鸽给她出了一道石子游戏,规则是这样的:有n堆石子排成一行,其中第i堆石子有ai个,叶妹妹可以选择做无数次这种操作:每次操作把连续相邻的k个石子堆中的每堆石子数目加一,请问叶妹妹能否让每堆石子的数目都相同呢?叶妹妹觉得这题太简单了,于是丢给了聪明的你,快来解决这个问题吧!

输入描述:

第一行输入样例组数TT对于每组样例来说,第一行输入两个数n和k第二行输入nn个数,其中第i个数为ai【数据规模与约定】1≤T≤10,1≤n≤10^5,1≤k≤n,0≤ai≤10^9

输出描述:

输出总共T行,对于每组样例来说,如果能使每堆石子的数目都相同则输出一个整数x,x表示达到相同时的最少的操作数;否则输出−1

示例1

输入

复制

1
4 3
1 1 1 2

输出

复制

1

区间+1一眼差分,关键在于区间选择的策略。

首先求出差分数组diff,题目描述等价于每次选取一个长度为k的区间使得diff[i] += 1, diff[i + k] -= 1,最终要diff[2~n]均为0,求最少操作次数。首先对于i: n - k + 2 ~ n的diff[i]如果有负数则无解,因为要让它变为0,操作区间的右端点会超过n + 1(注意diff[n + 1]--这样是可以的)所以这段区间只能减不能加。因此先遍历i: n - k + 2 ~ n,如果有小于0的无解,有大于0的根据差分原则先diff[i - k] += diff[i];再把diff[i]设为0.

经过上述操作,如果有解的话diff[n - k + 2 ~ n]的值应该已经变为0了。再处理n - k + 1 ~ 2。n - k + 1这个位置要特殊处理,如果其差分值小于0的话可以直接令其等于0同时累加答案(因为diff[n + 1]可以随意搞),大于0的话和之前类似,先判断i - k是否越界再累加答案。对于i属于[2, n - k + 2)处理方法和i = n - k + 2时基本一样,不同之处在于diff[i]小于0的时候如果(n - k + 1 - i) % k == 0,那么也是可以通过操作把它变为0的,然后ans += (-diff[i]) * ((n - k + 1 - i) / k + 1)。这里比较难理解,举个例子:

8 3
1 1 0 1 1 1 1 1
差分后得1 0 -1 1 0 0 0 0

n - k + 2为7,此时diff[7]为0,直接跳过...直到diff[4] > 0,此时把diff[4]置0,diff[1]++。再往下是diff[3],diff[3]小于1,而3 - 3 = 0越界了。如果没有上面提到的那个处理的话会输出-1,但是真的无解吗?我们看这个序列,操作顺序是[1, 3], [3, 5], [6, 8],有解。上面提到过diff[n + 1]可以随意搞,这启发我们可以借助diff[n + 1]和diff[n - k + 1]这两个位置。还是上面那个例子,处理到diff[3]的时候把diff[3]++,这样diff[6]就得--,此时diff[6]是-1,而diff[n - k + 1]可以随意搞(影响的是diff[n + 1],无所谓),因此我们再把diff[6]++即可。对于所有的满足(n - k + 1 - i) % k == 0的i都可以这样(本质上是传递的),累加答案的时候操作次数记得注意。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <map>
#include <set>
using namespace std;
int n, k, a[100005], diff[100005];
int main()
{
	freopen("data.txt", "r", stdin);
	int t;
	cin >> t;
	while(t--)
	{
		cin >> n >> k;
		bool flag = 1;
		for(int i = 1; i <= n; i++)
		{
			cin >> a[i];
			diff[i] = a[i] - a[i - 1];
			if(i >= 2)
			{
				if(a[i] != a[i - 1]) flag = 0;
			}
		}
		int ans = 0;
		bool pd = 1;
		if(!flag && n == k)
		{
			cout << -1 << endl;
			continue;
		}
		if(flag)
		{
			cout << 0 << endl;
			continue;
		}
		for(int i = n - k + 2; i <= n; i++)
		{
			if(diff[i] < 0)
			{
				pd = 0;
				break;
			}
			if(diff[i] > 0)
			{
				diff[i - k] += diff[i];
				ans += diff[i];
				diff[i] = 0;
			}
		}

		for(int i = n - k + 1; i >= 2; i--)
		{
			if(i == n - k + 1)
			{
				if(diff[i] < 0)
				{
					ans -= diff[i];
					diff[i] = 0;

				}
				else if(diff[i] == 0)
				{

				}
				else
				{
					if(i - k >= 1)
					{
						diff[i - k] += diff[i];
						ans += diff[i];
						diff[i] = 0;
					}
					else
					{
						pd = 0;
					}
				}
				continue;
			}
			if(diff[i] < 0)
			{
				if((n - k + 1 - i) % k == 0)
				{
					ans += (-diff[i]) * ((n - k + 1 - i) / k + 1);
					//参考
					//8 3
					//1 1 0 1 1 1 1 1

					//差分后是1 0 -1 1 0 0 0 0
					diff[i] = 0;
					continue;
				}
				pd = 0;
				break;
			}
			else if(diff[i] == 0) continue;
			else
			{
				if(i - k >= 1)
				{
					diff[i - k] += diff[i];
					ans += diff[i];
					diff[i] = 0;
				}
				else
				{
					pd = 0;
					continue;
				}
			}
		}
		if(!pd) cout << -1;
		else cout << ans;
		cout << endl;
	}
	return 0;
}
//4 3
//2 1 1 1
原文地址:https://www.cnblogs.com/lipoicyclic/p/14432090.html