zoukankan      html  css  js  c++  java
  • 【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
    ",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

  • 相关阅读:
    利用杨辉三角和阶乘计算组合数
    验证字符串是否为回文数
    利用线性同余产生伪随机数+可变参数使用
    根据RandomStr.java:使用类型转换生成六位验证字符串。
    Java语言基础问题
    从命令行输入参数值,输出求和值。
    愚公移山_节选(伪代码)
    CodeForces
    CodeForces
    E
  • 原文地址:https://www.cnblogs.com/yelir/p/11524184.html
Copyright © 2011-2022 走看看