2019.5.31普及模拟赛总结/题解分析

比赛开始,按照惯例,我把5题都看了一遍,认为按原有顺序做题应该是一种正确的做题顺序,就开始做题了


T1:Classroom Watch (num.cpp)

【问题描述】

给出一个正整数 n n n,现在问存在多少个 x x x,使得 x x x在十进制下的每一位之和加上 x x x 等于 n n n

【输入】

共 1 行,一个正整数 n n n

【输出】

第一行输出一个整数 m m m,表示有 m m m个符合条件的 (若没有符合条件的 ,请只输出一个 0 0 0)。
下面 m m m行,每行一个 x , x x ,x xx按从小到大输出。

心路历程
对于这道题,我认为我充分做到了 O l d Old Old L i u Liu Liu的话——将简单的题目复杂化。
我拿到这道题,经过 10 m i n 10min 10min的思考,我列出了一个方程,成功走向弯路。
还好,经过好长一段时间,我对我的思路产生了疑惑,又重新将题目好好读了一遍,静下心来用心思考后找到了正确的道路,将思路归为正轨。

题目分析
我们仔细观察题目条件,在十进制下,那么对于任意正整数n,那么每个数位范围一定是 0...9 0...9 0...9,各数位累加和最多不会超过 9 ∗ 9 = 81 9*9=81 99=81.
所以我们只需要枚举 n − 100 n-100 n100 n n n的所有数,验证一下即可.

启示有时候,题目就已经告诉你正解是什么了,由此要仔细读题,不放过一个字,既是为了能跟快相出正解,也是为了避免一些不必要的读题错误,让此次比赛无憾

代码:
#include <bits/stdc++.h>
using namespace std;
int n, sum = 0, tot = 0;
int a[1000];

int main() {
	freopen("num.in","r",stdin);
	freopen("num.out","w",stdout);
	scanf("%d",&n);
	for (int i=max(n-300,1);i<=n;i++) {
		int k = 0, j = i;
		while (j) {
			k = k + j%10;
			j=j/10;
		}
		if (k+i == n) sum++,a[++tot]=i;
	}
	printf("%d\n",sum);
	for (int i=1;i<=tot;i++) printf("%d\n",a[i]);
	return 0;
}

T2:组合技能(combo.cpp)

题目描述:

蓝月商城出新技能书了!!
如果古天乐想购买“旋风斩”,则他需要花费 A A A元;如果古天乐想买“半月弯刀”,则需要 B B B元;如果古天乐两个一起买,则需要 C C C元。
蓝月的设计师非常有头脑,每样商品的利润都是相同的。即假设旋风斩和半月弯刀的成本为 a , b a,b a,b元,则 A − a = B − b = C − a − b A-a=B-b=C-a-b Aa=Bb=Cab
给出 A A A B B B C C C求出利润,数据保证为正数。

格式

输入第一行一个数 T ( T &lt; = 100 ) T(T&lt;=100) T(T<=100),表示T次询问。
接下来T行,每行三个数 A , B , C ( A , B , C &lt; = 2000 ) A,B,C (A,B,C &lt;= 2000) A,B,C(A,B,C<=2000)
输出T行,每行一个数,表示利润。

Input :

3
275 214 420
6 9 11
199 199 255

Output:

69
4
143

心路历程
这题十分水,看完题目我很快就得出了结论—— 利 润 = A + B − C 利润=A+B-C =A+BC

题目分析
设利润为 x x x
A − a = B − b = C − a − b A-a=B-b=C-a-b Aa=Bb=Cab
A − a + B − b = 2 x , C − a − b = x A-a+B-b=2x,C-a-b=x Aa+Bb=2x,Cab=x
A − a + B − b − x = C − a − b A-a+B-b-x=C-a-b Aa+Bbx=Cab
x = A + B − C x=A+B-C x=A+BC

代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
	freopen("combo.in","r",stdin);
	freopen("combo.out","w",stdout);
	int T;
	scanf("%d",&T); 
	while (T--) {
		int a, b, c;
		scanf("%d%d%d",&a,&b,&c);
		printf("%d\n",a+b-c);
	}
}

T3:表面积(surface.cpp)

题目描述:

古天乐在搭积木,积木图可以抽象为一个 n ∗ m n*m nm的网格图,其中第 ( i , j ) (i,j) (i,j)的位置有 A i , j A_{i,j} Ai,j个积木。求表面积。

格式:

输入第一行两个数 n , m n,m n,m,接下来 n n n行每行 m m m个数,表示 A i , j A_{i,j} Ai,j
输出一个数,表示表面积。

范围:

1 &lt; = n , m &lt; = 100 1&lt;=n,m&lt;=100 1<=n,m<=100
1 &lt; = A i , j &lt; = 100 1&lt;=A_{i,j}&lt;=100 1<=Ai,j<=100

Input 1:

1 1
1

Output 1:

6

Input 2:

3 3
1 3 4
2 2 3
1 2 4

Output 2 :

60

心路历程
我也不知道我考试时候怎么想的,满脑浆糊,思路不清就开始写代码了。
于是我就炸了。

题目分析
我们可将所有木块的总表面积算出来。
然后将重叠的木块的表面积减去,就是我们所要的题目的解。
总面积为 s u m ∗ 6 sum * 6 sum6 ( s u m sum sum为总积木数)。
对于上下重叠: 重叠的部分面积为 ( A i , j − 1 ) ∗ 2 (A_{i,j}-1) * 2 (Ai,j1)2
对于左右,前后:重叠的部分面积可以通过 f o r for for循环模拟得出。

代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 200, M = 200;
int a[N][M];
int n, m;
int f[N+100];

int main() {
	freopen("surface.in","r",stdin);
	freopen("surface.out","w",stdout);
	int sum = 0, ans = 0;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) {
		for (int j=1;j<=m;j++) {
			scanf("%d",&a[i][j]);
			sum += a[i][j] * 6;
			ans += 2 * (a[i][j]-1);
		}
	}
	for (int i=1;i<=n;i++)
    	for (int j=2;j<=m;j++)
    		ans += min(a[i][j], a[i][j-1]) * 2;
    for (int j=1;j<=m;j++)
    	for (int i=2;i<=n;i++)
    		ans += min(a[i][j], a[i-1][j]) * 2;
    printf("%d",sum - ans);
}

T4 :红皇后的旅行(redqueen.cpp)

题目描述:

给定一个 n ∗ n n*n nn的棋盘,行和列标号为 0 , 1 , 2 , … . , n − 1 0,1,2,….,n-1 0,1,2,.,n1。在棋盘的 ( i s t a r t , j s t a r t ) (i_{start},j_{start}) (istart,jstart)位置上有一位红皇后,每次红皇后可以往六个方向走,如图所示:

在这里插入图片描述
现在红皇后想去 ( i e n d , j e n d ) (i_{end},j_{end}) (iend,jend)点,求最短距离,并且输出一条路径。
显然最短路径有无穷条,请按照以下顺序来搜索: U L , U R , R , L R , L L , L UL, UR, R, LR, LL, L UL,UR,R,LR,LL,L
如果无解,输出 I m p o s s i b l e Impossible Impossible

格式:

输入第一行一个数n,第二行四个数,i_start,j_start,i_end,j_end。
输出第一行一个数,最小步数,第二行输出方案。

范围:

5 &lt; = n &lt; = 200 5&lt;=n&lt;=200 5<=n<=200
0 &lt; = i s t a r t , j s t a r t , i e n d , j e n d &lt; = n 0&lt;=i_{start},j_{start},i_{end},j_{end}&lt;=n 0<=istart,jstart,iend,jend<=n
t h e the the s t a r r t i n g starrting starrting a n d and and e n d i n g ending ending p o s i t i o n position position a r e are are d i f f e r e n t different different.

Input 1:

7
6 6 0 1

Output 1:

4
UL UL UL L
在这里插入图片描述

Input 2:

6
5 1 0 5

Output 2:

Impossible

Input 2:

7
0 3 4 3

Output 2:

2
LR LL

在这里插入图片描述
心路历程
这道题很显然就是裸的BFS,看完题目,我就开始码代码了。
但是,我犯了一个致命的错误,对于初始化,我忘记了对开始点的标记。
以至于我的程序一直死循环,45min过去了,我仍然没有改出错误,于是先看下一题。
(我到比赛结束都没有找出错误)

题目分析
使用BFS持续向外扩展,并同时记录路径和当前步数。
最后,由终点向起始点输出。

启示当找不到程序错误时,去看看那些微小的地方,说不定,错误就错在那里

代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 40010, M = 300;
int fx[20] = {-2,-2,0,2,2,0};
int fy[20] = {-1,1,2,1,-1,-2};
struct egde {
	int x, y;
}q[N];
int n, f[M][M];
int h = 0, t = 0;
int sti, stj,edi,edj;
int prei[M][M], prej[M][M], num[M][M], run[M][M];

void write(int x,int y) {
//	cout<<prei[x][y]<<' '<<x<<' '<<y<<endl;
	if (!prei[x][y]) return;
	write(prei[x][y], prej[x][y]);
	if (num[x][y] == 0) printf("UL ");
	if (num[x][y] == 1) printf("UR ");
	if (num[x][y] == 2) printf("R ");
	if (num[x][y] == 3) printf("LR ");
	if (num[x][y] == 4) printf("LL ");
	if (num[x][y] == 5) printf("L ");
}

void bfs() {
	q[++t].x = sti;
	q[t].y = stj;
	f[sti][stj] = 1;
	while (t>=h) {
		int nowi = q[++h].x, nowj = q[h].y;
		for (int i=0;i<=5;i++) {
			int x = nowi + fx[i], y= nowj + fy[i];
			if (x <= 0 || y > n || y <= 0 || x > n) continue;
			if (!f[x][y]) {
				f[x][y] = 1;
				q[++t].x = x;
				q[t].y = y;
				prei[x][y] = nowi, prej[x][y] = nowj;
				num[x][y] = i, run[x][y] = run[nowi][nowj] + 1;
				if (x == edi && edj == y) {
					printf("%d\n",run[x][y]);
					write(edi,edj);
					return;
				}
			}
		} 
	}
	printf("Impossible");
}

int main() {
	freopen("redqueen.in","r",stdin);
	freopen("redqueen.out","w",stdout);
	scanf("%d%d%d%d%d",&n,&sti,&stj,&edi,&edj);
	n+=1;sti+=1;stj+=1;edi+=1;edj+=1;
	bfs();
	return 0;
}

T5:构造序列(construct.cpp)

题目描述:

有一个长度为 n n n的序列 A A A,其中 A [ 1 ] = 1 , A [ n ] = x , A [ 2 … n − 1 ] A[1]=1,A[n]=x,A[2…n-1] A[1]=1,A[n]=xA[2n1]可以是 1 1 1 k k k间任意一个正整数。求有多少个不同的序列,使得相邻两个数不同。
答案 1 0 9 + 7 10^9+7 109+7取模。

格式:

输入共一行,包含三个数, n , k , x n,k,x n,k,x
输出一个数,表示答案。

范围:

3 &lt; = n &lt; = 1 0 5 3&lt;=n&lt;=10^5 3<=n<=105
2 &lt; = k &lt; = 1 0 5 2&lt;=k&lt;=10^5 2<=k<=105
1 &lt; = x &lt; = k 1&lt;=x&lt;=k 1<=x<=k

Input 1:

4 3 2

Output 1:

3

心路历程
先考虑如果没有 A [ 1 ] A[1] A[1] A [ n ] A[n] A[n]的限制,怎么办?
很显然,答案就是 k ∗ ( k − 1 ) ( n − 1 ) k*(k-1)^{(n-1)} k(k1)(n1)
因为这道题很显然是递推,所以我就从各种方面去尝试寻找突破口。
时间好快, 1 h 1h 1h 30 m i n 30min 30min过去了,我仍然没有退出来。
比赛即将结束…

题目分析
我们设 f i , j f_{i,j} fi,j为在前i个空中,第 i i i个空填 j j j的方案数。
显然 f i , j = f i − 1 , 1 + f i − 1 , 2 + f i − 1 , 3 + . . . . + f i − 1 , k − f i − 1 , j f_{i,j}=f_{i-1,1}+f_{i-1,2}+f_{i-1,3}+....+f_{i-1,k}-f_{i-1,j} fi,j=fi1,1+fi1,2+fi1,3+....+fi1,kfi1,j;
因为我们只需要 f i , x f_{i,x} fi,x,我们就可简化为 f i = f i − 1 ∗ ( k − 1 ) f_{i}=f_{i-1} *(k-1) fi=fi1(k1)
我们再设 f x [ i ] fx[i] fx[i]为第i个数为x时的方案数。
那就有了以下式子: f x [ i ] = f [ i − 1 ] − f x [ i − 1 ] fx[i]=f[i-1]-fx[i-1] fx[i]=f[i1]fx[i1]
那么 f x [ n ] fx[n] fx[n]就是我们需要的答案。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值