第一题:灌溉
题目描述
小蓝负责花园的灌溉工作。
花园可以看成一个 n 行 m 列的方格图形。中间有一部分位置上安装有出水管。
小蓝可以控制一个按钮同时打开所有的出水管,打开时,有出水管的位置可以被认为已经灌溉好。
每经过一分钟,水就会向四面扩展一个方格,被扩展到的方格可以被认为已经灌溉好。即如果前一分钟某一个方格被灌溉好,则下一分钟它上下左右的四个方格也被灌溉好。
给定花园水管的位置,请问 k 分钟后,有多少个方格被灌溉好?
输入描述
输入的第一行包含两个整数 n,m。
第二行包含一个整数 t,表示出水管的数量。
接下来 t 行描述出水管的位置,其中第 i 行包含两个数 r,c 表示第 r 行第 c 列有一个排水管。
接下来一行包含一个整数 k。
其中,1≤n,m≤100,1≤t≤10,1≤k≤100。
输出描述
输出一个整数,表示答案。
输入输出样例
输入
3 6
2
2 2
3 4
1
输出
9
dfs思路,就是每次把有水的地方进行扩大
并且设置状态s,表示是第几次开始扩大的,这样每一次就能做到该次只扩大该一次的
#include<iostream>
#include<queue>
using namespace std;
const int N = 110;
int n, m, t, k;
int g[N][N];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1};
void dfs(int r, int c, int s){
for(int i = 0; i < 4; i++){
int x = dx[i] + r, y = dy[i] + c;
if(x >= 1 && x <= n && y >= 1 && y <= m && g[x][y] == 0)
g[x][y] = s + 1;
}
}
int main(){
scanf("%d%d", &n, &m);
scanf("%d", &t);
while(t--){
int a, b;
scanf("%d%d", &a, &b);
g[a][b] = 1;
}
scanf("%d", &k);
int s = 1;
while(s <= k){
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(g[i][j] == s) {
dfs(i, j, s);
}
s++;
}
int ans = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(g[i][j] >= 1)
ans++;
cout<<ans<<endl;
return 0;
}
第二题:小朋友崇拜圈
题目描述
班里 N 个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,⋯N。
输入描述
输入第一行,一个整数 N(3<N<10 5 )。
接下来一行 N 个整数,由空格分开。
输出描述
要求输出一个整数,表示满足条件的最大圈的人数。
输入输出样例
输入
9
3 4 2 5 3 8 4 6 9
输出
4
dfs,状态有三个,当前位置,当前位置的崇拜者,人数
枚举每个位置
当人数大于总人数肯定是死循环了,return
当崇拜者等于枚举的当前位置,即构成一个崇拜圈
更新最大值
其余情况继续dfs
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n, p[N];
int ans;
void dfs(int u, int v, int t){
if(t > n) return ;
if(v == u) {
ans = max(ans, t);
return ;
}
dfs(u, p[v], t + 1);
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
for(int i = 1; i <= n; i++)
dfs(i, p[i], 1);
cout<<ans<<endl;
return 0;
}
第三题:括号序列
题目描述
给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。
两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。
例如,对于括号序列 ((()(((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()()()()、()(())()(())、(())()(())()、(()())(()()) 和 ((()))((()))。
输入描述
输入一行包含一个字符串 s,表示给定的括号序列,序列中只有左括号和右括号。
输出描述
输出一个整数表示答案,答案可能很大,请输出答案除以 1000000007 (即 109+7)的余数。
输入输出样例
输入
((()
输出
5
括号问题两个条件:
左括号等于右括号
左括号一定大于等于右括号的数量才能加
独立性:放置左右括号方案互相不影响
所以可以先左括号放,再进行右括号放置(相当于反着放左括号),这里翻转加左右变换得到反序列
状态:f[i,j]表示前i个括号,左括号比右括号多j个的方案数量
然后方案数量 f[0,0] = 1
集合:左括号,f[i][j] = f[i-1][j-1];
右括号,f[i][j] = f[i-1][j+1] + f[i][j-1]
还要取余MOD
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5010, MOD = 1e9 + 7;
typedef long long LL;
char str[N];
LL f[N][N], n;
LL call(){
memset(f, 0, sizeof f);
f[0][0] = 1;
for(int i = 1; i <= n; i++){
if(str[i] == '('){
for(int j = 1; j <= n; j++)
f[i][j] = f[i-1][j-1];
}
else{
f[i][0] = (f[i-1][0] + f[i-1][1]) % MOD;
for(int j = 1; j <= n; j++)
f[i][j] = (f[i-1][j+1] + f[i][j-1]) % MOD;
}
}
for(int i = 0; i <= n; i++)
if(f[n][i])
return f[n][i];
return -1;
}
int main(){
scanf("%s", str + 1);
n = strlen(str + 1);
LL l = call();
reverse(str + 1, str + 1 + n);
for(int i = 1; i <= n; i++)
if(str[i] == '(') str[i] = ')';
else str[i] = '(';
LL r = call();
cout<<l * r % MOD<<endl;
return 0;
}
第四题:砍竹子
砍竹子 - 蓝桥云课 (lanqiao.cn)
每次变为sqrtl(x/2+1),1e18只需要6次即可
思维题
考虑同一层的相邻的,是不是总次数就要减一
所以先计算每一课树,需要多少次,也就是多少层
再把相邻的减去
这里记住sqrt函数
sqrtl 计算long
sqrt 计算double
sqrtf 计算 float
#include<iostream>
#include<math.h>
using namespace std;
const int N = 200010, M = 10;
typedef long long LL;
LL f[N][M];
int n, m;
int main(){
scanf("%d", &n);
LL stk[N];
int res = 0;
for(int i = 0; i < n; i++){
LL x;
scanf("%lld", &x);
int top = 0;
while(x > 1) stk[ ++ top] = x, x = sqrtl(x / 2 + 1);
res += top;
m = max(m, top);
for(int j = 0, k = top; k ; j++, k--)
f[i][j] = stk[k];
}
for(int i = 0; i < m; i++)
for(int j = 1; j < n; j++)
if(f[j][i] && f[j][i] == f[j-1][i])
res--;
cout<<res<<endl;
return 0;
}