ACM招新赛<赛后题解与反思总结>⑤

问题 C: 象棋中的炮

题目描述

在中国象棋中正所谓新手玩车,熟手玩炮,老手玩马,由此可见象棋中炮的地位还是比较高的。
给定一个n×m的棋盘,全部摆满炮,我们视所有炮都不属于同一阵营,他们之间可以相互攻击但不能不进行攻击直接移动。
请问经历若干次攻击,直到不能攻击后,最少能剩余多少个炮。

输入描述

第一行一个正整数t,表示数据组数,(1<=t<=105)
对于每组数据,输入两个正整数n和m,(1<=n,m<=109)

输出描述

对于每组数据,输出最少能剩余多少个炮。

样例输入

2
1 2
2 3

样例输出

2
4
        炮是隔一个格子吃掉下一个 (我就是在这错掉的,没玩过不知道哇ε(┬┬﹏┬┬)3
 然后我们知道这个棋盘是规则的且上面全是炮,
那么我们可以 选取一行和一列的炮 (假设我们选的是最上面一行和最右面一列)
        我们让 最上面 这行一直 往里面吃 (就是只要我最外面这行还可以吃,那就往里面吃)
那么最后就会剩下最多两行,因为两行就没办法吃了。
        同样的,我们让 最右面 的这列一直往里面吃,最后最多会剩下 2 列,
也就是说如果 n>=2 且 m>=2最后会剩下一个 2*2 的格子还有炮,那就是只会剩下 4 个炮。
        至于为什么要说 n>=2 且 m>=2 呢?
因为 如果 n 或 m 其中一个只有 1,那么他会始终吃不了,只能让列去进行往里面吃的操作,最后会剩下 1*2 的格子还有炮,
如果 n 和 m 都是 1,那就只有 1*1 的格子了。
分成这三种情况去讨论即可。
#include
int main()
{
    int t,m,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        //1.m=1&&n=1
        if(m==1&&n==1)
        {
            printf("1\n");
        }
        if(m==1||n==1)
        {
            printf("2\n");
        }
        if(m>=2&&n>=2)
        {
            printf("4\n");
        }
    }
}

问题 E: 图形最高分

题目描述

zq和铨儿喜欢玩平板游戏,游戏一开始屏幕上会出现很多个图形,玩家在每一轮可以合并两个图形,当只有一个图形的时候游戏结束,每个图形都有一个大小,合并完成后的图形的大小为x+y,x和y分别为合并之前的两个图形,与此同时,玩家会获得x*y的分数。
zq和铨儿新开了一盘游戏,屏幕上出现了n个图形,每个图形的大小已知,请算出zq和铨儿最大能获得的分数

输入描述

第一行输入一个整数n (2≤n≤100)
第二行输入n个整数范围在1到100之间

输出描述

输出一个整数代表zq和铨儿最大能获得的分数

样例输入

3
1 2 3

样例输出

11
        这题要用 贪心 的思维来解答
        因为我们每次获得的分数是 x*y,那么我们肯定让 x 和 y 变的尽可能的大,那么分数就是最大的.
        1.排序:把图形的大小 从小到大 排个序,取出其中 最大的和次大的来合并
        2.循环:之后的新图形再去和剩下的图形里面的最大的图形合并
这样就能保证我们每次进行合并的时候所选用的 x 和 y都是数组中最大的图形。
#include 
int main()
{
	int n;
	scanf("%d", &n);
	int a[n];
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
	}
	//冒泡排序得到最大值
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (a[j] > a[j + 1])
			{
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	int sum = 0;
	for (int i = n - 1; i >0; i--)
	{
		sum = a[i] * a[i - 1] + sum;//分数
		a[i - 1] = a[i] + a[i - 1];//最大的和次大合并
	}
	printf("%d\n", sum);//最大能获得的分数
}

注意:int a[n];在VS不能这样写在这个编译器为了安全禁止了 

在VS上可以这样写

#include 
int a[110];
int main()
{
	int n;
	scanf("%d", &n);
	
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
	}
	//冒泡排序得到最大值
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - 1 - i; j++)
		{
			if (a[j] > a[j + 1])
			{
				int temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}
	}
	int sum = 0;
	for (int i = n - 1; i > 0; i--)
	{
		sum = a[i] * a[i - 1] + sum;//分数
		a[i - 1] = a[i] + a[i - 1];//最大的和次大合并
	}
	printf("%d\n", sum);//最大能获得的分数
}

问题 F: zq关不上灯啦!

题目描述

zq有一盏台灯,台灯有三种颜色分别是 “冷色”, “暖色”, “混合色”。

台灯有一个开关,如果当前开关处于关闭状态,那么拨动一下开关,台灯就会变成“冷色”,再拨动一下就会变成“暖色”,再拨动一下就会变成“混合色”,再拨动一下台灯就会关闭。

调皮的zq使用台灯时,总是喜欢连续拨动k下开关,例如k=2时,zq每次拨动开关都只会连续拨动2下。
zq现在想知道,当台灯目前的状态处于n时(下方的输入描述有解释),他需要几次“连续拨动k下开关”,才能将台灯关闭,或者他永远无法关闭台灯。

输入描述

本题有多组测试数据。
第一行一个正整数t表示数据组数,(1<=t<=105)
接下来t行,每行两个整数 n,k(0≤n≤3;1≤k≤109),分别表示台灯的初始状态,以及zq每次会连拨开关的次数。
台灯各状态表示说明如下:
- 当n=1时,台灯为“冷色”;
- 当n=2时,台灯为“暖色”;
- 当n=3时,台灯为“混合色”;
- 当n=0时,台灯处于关闭状态。

输出描述

输出t行,每行一个整数 x,表示zq最少尝试x次的连拨开关可以关闭台灯。如果zq永远不能关闭台灯则输出-1。

样例输入

3
1 5
2 4
0 2

样例输出

3
-1
0

提示


第一个样例中,zq先“连续拨动5下开关”,此时台灯会从冷色变成暖色,然后zq再次“连续拨动5下开关”,此时台灯会变成混合色,zq再再次“连续拨动5下开关”,此时台灯恰好会被关闭。zq用了3次“连续拨动5下开关”,因此答案为3。


第二个样例中,zq永远无法关闭台灯,因此输出 -1。


第三个样例中,台灯本身就是关闭的,因此答案为0。

 这题我们不要管每次会连拨开关的次数 k 到底有多大,我们只要知道他每次连拨开关后灯的状态会变化多少就行了,举个栗子:如果 n=1,k=2,那么一次连拨开关后 n 会变化 2 个(n=3),如果 k=6,连拨开关后 n 还是只会变化两个。也就是说,只要考虑 k%4 后的值是多少就行(为什么是 4 呢?因为台灯就 4 个状态,自己好好想想为什么)知道了 k 会让台灯变化几个状态,那么我们直接去模拟这个过程看看几次 k 之后 n 等于 0 就行。但是有个挖坑的点,还是举个栗子:n=1,k=2,那么下一次变化后 n=3,再变化之后 n 又是 1 了,嗬!这不就一直运动下去一直关不了了嘛!

#include
int main()
{
	int t,n,k,x;
	scanf("%d", &t);
	// n台灯的初始状态,k每次会连拨开关的次数。
	while (t--)
	{
			scanf("%d %d", &n, &k);
	
		//当n=1时,台灯为“冷色”
		//当n = 2时,台灯为“暖色”
		// 当n = 3时,台灯为“混合色”
		//当n = 0时,台灯处于关闭状态
		for (int i = 0; i < 4; i++)//4次:
		{
			if ((i * k + n) % 4 == 0)
			{
				x = i;
				break;
			}
			else
				x = -1;
		}
		printf("%d\n", x);//x=连续x次拨动k下开关
	}
	//输出:表示zq最少尝试x次的连拨开关可以关闭台灯。如果zq永远不能关闭台灯则输出-1。
	return 0;
}

问题 G: 地道地道地地道道

题目描述

zq学长早上起来很想喝豆汁儿,于是他前往了买豆汁儿的路上,但是zq学长刚刚睡醒脑袋有点懵,请你帮他判断能否喝到豆汁儿。
买豆汁儿的路为n个格子(类似大富翁那种,但是是一条直线排列),每个格子上都有一个数字,格子上的数字代表zq学长可以下一步走到第几个格子
zq学长一开始站在第一个格子上,豆汁儿所在的格子上的数字为-1。
如果在多次移动后zq学长可以喝到豆汁儿请输出:真地道
如果不能则输出:不太牛

输入描述

第一行一个整数n代表路上的格子数量(1<=n<=105)
第二行n个数字,第i个数字代表第i个格子上的数字(1<=格子数字<=n)

输出描述

喝到豆汁儿请输出:真地道
不能则输出:不太牛

样例输入

6
2 3 4 5 -1 6

样例输出

真地道

提示

针对于样例:

一开始zq站在第一个格子,格子数字为2,则下一步zq可以到达第2个格子

之后,zq站在第二个格子,格子数字为3,则下一步zq可以到达第3个格子

之后,zq站在第三个格子,格子数字为4,则下一步zq可以到达第4个格子

之后,zq站在第四个格子,格子数字为5,则下一步zq可以到达第5个格子

之后,zq站在第五个格子,格子数字为-1,则zq已经喝到豆汁儿了

        因为每个格子上的数字是固定的,所以呢,zq 他所走的路也只会是唯一的。
        整个循环,每次更新一下 zq 所在的位置,然后一直跑下去直到遇到格子数字为-1 为止?
看似正确,但是暗藏玄坤。
        如果第一个格子的数字是 2,第二个格子数字是 1,那么 zq 就会在这俩格子上面反复横跳,一直跑不出来落得个时间超限的下场。
        那么有没有什么办法来解决这个问题呢?我们来这样想:
        既然走过的路径是唯一的,假设我走到了某个格子上面后,过了一会儿我又走到这个格子上面了,那么因为路径是唯一的,所以我接下来还会重复刚刚走过的路,然后一直绕下去。
        也就是说,zq 绝对不走到已经走过的格子,要不然他就会一直绕下去,那么我们开个数组来记录所有格子的状态,当 zq 走过后就让这个格子的状态变为走过的样式,然后如果之后再走到这个格子 zq 看到这个格子被走过了就知道会绕下去(也就走不到-1 了),这时候直接跳出循环就行。

#include
int main()
{
    int n,t,tem=0;
    scanf("%d",&n);
    int a[n];
    for(int i=0;i

你可能感兴趣的:(ACM招新赛,算法)