【csp模拟赛1】不服来战 (challenge.cpp)

【题目描述】

最近小 Z 和他的朋友都迷上了一款手机游戏:不服来战。 游戏的设定十分简单,在游戏开始时,会给出一排共 N 个灯,有的灯是开着 的有的是关着的,每个灯都有一个分数。而玩家可以进行任意次操作,每次操作 改变连续 K 盏灯的开关状态。尽管机智如小 Z 也总是没法得到最高分,没法把他 的朋友 PK 下来。于是他来向你请教,希望知道在不同情况下,最高分分别是多 少。

【输入格式】

第一行,一个正整数 T,表示测试数据组数。 对于每组测试数据: 首先是一行两个正整数 N,K,意义如题目所述。 接着是 N 个数依次表示 1~N 每盏灯的开关状态。每个数是 0 或 1,分别 表示 这盏灯初始时是关的或开的。 最后是 N 个非负整数依次表示 1~N 每盏灯亮着的得分。

【输出格式】

对每组测试数据输出一行。表示最大的得分。

【样例输入】

3 3 2 0 1 1 3 2 4 10

1 0 0 1 0 1 0 0 1 0 1

1 1 1 3 4 3 4 5 1 5 10 7

1 1 1 1 1 1 1 1 1 1

5 5 3 3 4

3 5 1 1 3

【样例输出】

7

28

33

【样例解释】

第一组数据:翻转前 2 盏灯是最优方案; 第二组数据:你可以把所有的灯点亮; 第三组数据:不做任何操作是最优方案

思路:

不从图的角度思考也可以得出正解。

考虑操作 i 和操作 i+1 同时按下,这时产生的 效果时把第 i 盏灯和第 i+K 盏灯翻转。

也就是说,我们得到了一种新的操作,这种操作是, 把任意相距为 K 的两盏灯同时翻转。

不难发现,以下两种操作可以等价于原题中的操作 “翻转任意连续 K 盏灯的状态”:

(1)同时翻转 1~K 盏灯

(2)同时翻转任意距离为 K 的两盏灯 等价性是因为,操作(2)可以由原题中的操作得来;而原题中“翻转任意连续 K 盏 灯的状态”可以通过一次(1)和若干次(2)组合而来。

下面我们只考虑新的操作(1)(2)。

首先,操作(1)是否进行可以枚举。这时,我 们只需考虑操作(2)。而操作(2)似乎把一列灯也分成了许多组。以 N=10, K=3 为例,

灯 1,4,7,10 是一组;

灯 2,5,8 是一组;

灯 3,6,9 是一组。

每次进行操作(2),等价于翻转一组内相邻的两盏灯! 组与组之间没有影响! 再稍作分析,不难发现,如果一组内“初始时是关的”的灯为偶数个,那么我们可以 做到只把它们全部打开,也就是说打开了这一组所有的灯;如果一组内“初始时是关的” 的灯为奇数个,这个时候我们不能把所有的灯都打开,只好作出让步,选择放弃亮度最小 的那盏灯。每一组都如此处理,最后加起来。

解法一、二的时间复杂度都是 O(N)的。

代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
const int MAXN = 1000000;
using namespace std;
int T,n,k,val[MAXN],vis[MAXN],sum; 
inline int read()
{
	int x = 0,f = 1; char s = getchar();
	while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
	return x * f;
}
void clean()
{
	memset(val,0,sizeof(val));
	memset(vis,0,sizeof(vis));
	sum=0;
}
int get1()
{
	int ans1=sum;
	for(int i=1;i<=k;i++)
	{
		int tot=0,mi=2147483647;
		for(int j=i;j<=n;j+=k)
		{
			if(vis[j]==0)tot++;
			mi=min(mi,val[j]);
		}
		if(tot & 1)ans1-=mi;
	}
//	cout<<ans1<<endl;
	return ans1;
}
int get2()
{
	int ans2=sum;
	for(int i=1;i<=k;i++)vis[i]^=1;
	for(int i=1;i<=k;i++)
	{
		int tot=0,mi=2147483647;
		for(int j=i;j<=n;j+=k)
		{
			if(vis[j]==0)tot++;
			mi=min(mi,val[j]);
		}
		if(tot & 1)ans2-=mi;
	}
//	cout<<ans2<<endl;
	return ans2;
}
void work()
{
	n=read();k=read();
	for(int i=1;i<=n;i++) vis[i]=read();
	for(int i=1;i<=n;i++) val[i]=read(), sum+=val[i];
	int s1=get1();
	int s2=get2();
	int ans=max(s1,s2);
	printf("%d\n",ans);
}
int main()
{
	#ifdef yilnr
	#else
	freopen("challenge.in","r",stdin);
	freopen("challenge.out","w",stdout);
	#endif
	T=read();
	while(T--)clean(),work();
	fclose(stdin);fclose(stdout);
	return 0;
}

  完毕:

        一lin

 

上一篇:Lyft Level 5 Challenge 2018 - Elimination Round


下一篇:Mujin Programming Challenge 2017题解