【题目描述】
最近小 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