1.路径计数
有一个n×n�×�的网格,有些格子是可以通行的,有些格子是障碍。
一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。
由于答案很大,输出对109+7109+7取模的结果。
输入格式
第一行一个正整数n�。
接下来n�行,每行n�个正整数,11表示可以通行,00表示不能通行。
输出格式
一个整数,表示答案。
本题是一道基础递归题,记忆化搜索即可
#include <iostream>
using namespace std;
const int N = 1e9 + 7;
int a[105][105], b[105][105], n;
int dfs(int x, int y)//深度优先搜索
{
int ans = 0;
if (x == n && y == n)
ans = 1;
else
{
if (b[x][y] != 0)//记忆化搜索,节省时间
ans = b[x][y];
else
{
if (x < n && a[x + 1][y] == 1)
ans += dfs(x + 1, y);
if (y < n && a[x][y + 1] == 1)
ans += dfs(x, y + 1);
ans %= N;
b[x][y] = ans;
}
}
return ans;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> a[i][j];
cout << dfs(1, 1);
return 0;
}
2
给定一个长度为 n 的数组 a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n并且 ap1<ap2<⋯<apm,使得ap1+ap2+⋯+apm最大。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
本题是一道动态规划基础题,使用滚动数组即可解决
#include <iostream>
using namespace std;
const int N = 1e5 + 5;
int a[N], n, f[N];
long long sum, ans;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
f[i] = a[i];
}
for (int i = 1; i <= n; i++)//滚动数组
{
for (int j = 1; j <i; j++)
{
if (a[i] >= a[j])
f[i] = max(f[i], a[i] + f[j]);
}
}
int ans = f[1];
for (int i = 1; i <= n; i++)
{
if (ans < f[i])
ans = f[i];
}
cout << ans;
}
3
给定一个整数 n。你需要对它做 m 次操作。在一次操作中,你要将这个数的每一位 d� 替换成 d+1。比如,19121912 在进行一次操作后将变成 2102321023。
请求出整数 n 进行了 m 次操作后的长度。答案可能很大,输出对 109+7109+7 取模后的结果。
输入格式
第一行一个整数 t,表示测试单元的个数。
接下来 t 行,每行有两个整数 n和 m,表示最初的数字和进行多少次操作。
输出格式
对于每个测试单元输出最终数字的长度,答案对 1e9+7 取模。
对于本题,我们可以发现经过每次加1后,数字i的数量即为加1前i-1的数量,(0的数量为加1前9的数量,1的数量为加1前9的数量和0的数量之和)发现了这个规律后,本题就比较简单了,需要注意的是,由于本体的输入量很大,需要用快读和printf输出防止超时
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 2e5 + 10, M = 1e9 + 7;
int a[10], p[N], q, t, x, y, m, i;
long long ans;
char b[10];
inline void read(int& a)
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch>'9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar();
}
a = s * w;
}
int main()
{
p[0] = 1;
a[0] = 1;
for (int i = 1; i <= N - 1; i++)//p[i]表示从0开始执行i次加1后数字的个数
{
p[i] = a[9] + p[i - 1];
q = a[9];
for (int i = 9; i>=2; i--)
{
a[i] = a[i - 1];
}
a[1] = a[0] + q;
a[0] = q;
a[1] %= M;
p[i] %= M;
}
cin >> t;
while (t--)
{
ans = 0;
i = 0;
scanf("%s", b);
read(m);
int x = strlen(b);
for (i=0; i<x; i++)
{
y = b[i] - '0';
ans += p[y + m];
}
ans %= M;
printf("%d\n",ans);
}
return 0;
}
4
平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:
- 刚开始,派派已经位于某传送阵之上;
- 如果派派掌握一种魔法 (A,B),其中 A,B均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y)瞬间移动至 (X+A,Y+B);
- 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法。
问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?
输入格式
第一行一个整数 N。
接下来一行 N 行,每行包含两个整数 Xi,Yi, 表示每个魔法阵的坐标。
输出格式
一个数,表示答案。
对于本题,我们可以发现,对于每一个距离(a,b),我们只需掌握a,
b除以它们最小公因数的魔法即可,此后如果有距离是该魔法的任意倍数,也可以该魔法解决,下面附上代码
#include <iostream>
#include <cmath>
#include <set>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e5;
typedef pair<int, int>ss;
set<ss>s;
int m, x[505], y[505], z, d, c, p, q, ans;
int main()
{
cin >> m;
for (int i = 1; i <= m; i++)
{
cin >> x[i] >> y[i];
}
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= m; j++)
{
if (j == i)
continue;
p = x[j] - x[i];
q = y[j] - y[i];
if (p == 0 || q == 0)
{
int u = 0;
s.insert({ p,q });
}
else
{
if (abs(p) % abs(q) == 0 || abs(q) % abs(p) == 0)//求最简魔法
{
z = min(abs(p), abs(q));
p /= z;
q /= z;
}
s.insert({p,q});
}
}
}
ans = s.size();
cout << ans;
return 0;
}
5
对于一个长度为 n 的0101序列 a1,a2,…,an。
你可以执行以下操作任意多次:
-
选择两个下标 1≤i,j≤n(i≠j)。
-
记x=ai xor aj , y=ai or aj, 其中 xor 表示按位异或 , or 表示按位或。
-
然后令 ai=x,aj=y或 ai=y,aj=x。
给定两个0101序列 s,t , 请你判断是否可以通过有限次(可以为00次)操作将序列 s变为 t。
输入格式
第一行一个整数 t, 表示数据的组数(1≤t≤103)。接下来 t 组数据:
每组第一行一个0101字符串 s(1≤|s|≤103),每组第二行一个0101字符串 t(1≤|t|≤103)。
注意:|s|可能不等于 |t|。
输出格式
如果可以通过有限次(可以为00次)操作将序列 s 变为 t , 输出 YES
, 否则输出 NO
。
对于本题,我们可以发现,在两个字符串长度相等的前提下,如果一个字符串全为0而另一个不全为0,则第一个字符串无法转化为第二个字符串,知道了这点后,本题就十分简单了。
#include <iostream>
#include <string.h>
using namespace std;
char a[3005], b[3005];
int main()
{
int t;
cin >> t;
while (t--)
{
int x = 0, y = 0,
cin >> a >> b;
if (strlen(a) == strlen(b))
{
for (int i = 0; i < strlen(a); i++)
{
if (a[i] == '0')
x++;
if (b[i] == '0')
y++;
}
if (x == strlen(a) && y != strlen(a) || x != strlen(a) && y == strlen(a))
cout << "NO" << '\n';
else
cout << "YES" << '\n';
}
else
cout << "NO" << '\n';
}
return 0;
}
6
我们称一个字符串为好字符串,指这个字符串中只包含0
和1
。
现在有一个好字符串,求这个字符串中1
恰好出现k次的子串有多少个。
输入格式
第一行给出一个数字k,表示子串中1
的个数。
第二行给出好字符串。
输出格式
输出一个整数,表示好字符串中有多少个符合条件的子串
数据范围
0≤k≤106, |s|≤106
对于本题,如果k=0的话,由数学知识可得,对于n个连续的0,字串的个数是n*(n+1)/2;
如果k不等于0的话,我们可以用一下代码解决
for (int i = 1; i <= x; i++)//表示从i开始的字串需要含有1的个数
{
a[i] = k;
}
int p=1;
for (int i = 0; i < x; i++)
{
if (b[i] == '0')
{
for (int t = p; t <= x; t++)//如果a[t]为0的话,出现0可使字串的数量加1,如果a[t]大于0的话则无影响,因为该字串含1的数量还不够。
{
if (a[t] > 0)
break;
if (a[t] == 0)
{
ans++;
}
}
}
else
{
for (int j = p; j <= i + 1; j++)
{
a[j]--;
if (a[j] == -1)//如果a[j]为-1的话,说明该字串含1的数量超出了k,可以放弃该子串了
p = j + 1;
}
}
}
for (int i = 1; i <= x; i++)//在加上每个符合条件的字串
{
if (a[i] <= 0)
ans++;
else
break;
}
下面附上完整代码
#include <iostream>
#include <string.h>
using namespace std;
const long long N = 1e6 + 5;
long long a[N], k, ans, p = 1;
char b[N];
long long xx(long long x)
{
long long t = x * (x + 1) / 2;
return t;
}
int main()
{
cin >> k;
cin >> b;
int x = strlen(b);
if (k != 0)
{
for (int i = 1; i <= x; i++)
{
a[i] = k;
}
for (int i = 0; i < x; i++)
{
if (b[i] == '0')
{
for (int t = p; t <= x; t++)
{
if (a[t] > 0)
break;
if (a[t] == 0)
{
ans++;
}
}
}
else
{
for (int j = p; j <= i + 1; j++)
{
a[j]--;
if (a[j] == -1)
p = j + 1;
}
}
}
for (int i = 1; i <= x; i++)
{
if (a[i] <= 0)
ans++;
else
break;
}
}
else
{
int uu = 0;
for (int i = 0; i < x; i++)
{
if (b[i] == '0')
{
uu++;
}
else
{
ans += xx(uu);
uu = 0;
}
}
ans += xx(uu);
}
cout << ans;
return 0;
}
7
现在有一个栈,有 n 个元素,分别为 1,2,…,n。我们可以通过 push
和 pop
操作,将这 n 个元素依次放入栈中,然后从栈中弹出,依次把出栈的元素写下来得到的序列就是出栈序列。
比如 n=3,如果执行 push 1, push 2, pop, push 3, pop, pop
,那么我们 pop
操作得到的元素依次是 2,3,1。也就是说出栈序列就是 2,3,1。
现在给定一个合法的出栈序列,请输出一个合法的由 push
和 pop
操作构成的操作序列。这里要求 push
操作一定是按 1,2,…,n的顺序。
输入格式
第一行一个整数 n。接下来一行 n 个整数,表示出栈序列。
输出格式
输出 2n行,每行一个 push
或 pop
操作,可以证明一个出栈序列对应的操作序列是唯一的。
本题是一道栈的基础题,了解栈的使用方法便可解决
#include <iostream>
#include <stack>
using namespace std;
stack <int>a;
int n, b[100005], j = 1;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> b[i];
}
a.push(0);
for (int i = 1; i <= n; i++)
{
cout << "push " << i<< '\n';
a.push(i);
while(a.top() == b[j])
{
if (a.top() == 0)
break;
a.pop();
cout << "pop" << '\n';
j++;
}
}
while (a.top() != 0)
{
cout << "pop" << '\n';
a.pop();
}
return 0;
}
8
您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:
-
每行的黑色方块数与白色方块数相同。
-
每列的黑色正方形数与白色方块数相同。
-
没有行或列具有 33 个及以上相同颜色的连续正方形。
给定网格,确定它是否正确。
输入格式
第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。
接下来 n 行,每行包含一个长度为n的由字符B
和W
组成的字符串,代表网格正方形的颜色。
输出格式
如果网格正确,请打印数字 1 在一行上。否则,请打印数字 0 在一行上。
本题是一道比较简单的题,直接上代码解释了、
#include <iostream>
using namespace std;
char a[25][25];
int b[25], c[25], n, x[25], y[25], z, t;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> a[i][j];
if (a[i][j] == 'W')//b[i]表示第i行w的数量,c[j]表示第j列w的数量
{
b[i]++;
c[j]++;
}
if (i >= 2 && a[i][j] == a[i - 1][j])//x[j]表示某列具有相同颜色数目的正方形的数量减1
{
x[j]++;
if (x[j] == 2)
t = 1;
}
else
{
x[j] = 0;
}
if (j >= 2 && a[i][j] == a[i][j-1])//y[i]同理
{
y[i]++;
if (y[i] == 2)
t = 1;
}
else
{
y[i] = 0;
}
}
}
for (int i = 1; i <= n; i++)
{
if (b[i] != n / 2)
{
t = 1;
break;
}
if (c[i] != n / 2)
{
t = 1;
break;
}
}
if (t == 0)
cout << 1;
else
cout << 0;
return 0;
}
9
Polycarp 有一个长度为 n的数组 a1,a2,...,an(n是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n) 使 ai减去 k。
在 Polycarp 进行若干次操作后(可能 00 次),数组 a中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1−1。
输入格式
第一行一个整数 t,表示测试单元的个数。
接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,...,an。
输出格式
对于每个测试单元输出单独一行一个整数 k (k≥1) —— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果 k能任意大的话。
直接上代码解释了
#include <iostream>
#include <cmath>
using namespace std;
int a[45], n, t, ans;
int divisor(int a, int b) //求最大公因数函数
{
int temp;
if (a < b)
{
temp = a; a = b; b = temp;
}
while (b != 0)
{
temp = a % b;
a = b;
b = temp;
}
return a;
}
int main()
{
cin >> t;
while (t--)
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 2; i <= n; i++)
{
ans = abs(a[i] - a[i-1]);
if (ans != 0)
break;
}
if (ans == 0)
cout << -1 << '\n';
else
{
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= n; j++)
{
if (a[j] == a[i])
continue;
int x = abs(a[j] - a[i]);
ans = divisor(x, ans);//如果两数都不为0的话,求两数的最大公因数
if (ans == 1)//1为最小答案,如果ans已经是1的话,可以直接结束
break;
}
}
cout << ans << '\n';
}
}
return 0;
}
10
给定一个字符串,你可以删除多个(可以是 0) 相同 的字符,这样操作之后,你能否得到一个回文串?如果能,求最小化删除的个数。
输入格式
多组数据。
每一组数据包含两行,分别为字符串的长度 N,以及一个仅由小写字母组成的字符串 S。
输出格式
对于每一组数据,输出一行。
如果不可能得到一个回文串,输出 −1。反之则输出最小操作次数。
#include <iostream>
#include <string.h>
using namespace std;
const int N = 1e5 + 5;
char a[N], p;
int n, t, ans=1,j,i,q,x,y;
void pp(char p)
{
while (y >= x + 1)
{
if (a[x] == a[y])
{
y--;
x++;
}
else
{
if (a[x] == p)
{
ans++;
x++;
}
else if (a[y] == p)
{
ans++;
y--;
}
else
{
ans = -1;
break;
}
}
}
}
int main()
{
cin >> t;
while (t--)
{
p = ' ';
ans = 0;
q = 0;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
j = n, i = 1;
while (j >= i + 1)
{
if (a[j] == a[i])
{
j--;
i++;
}
else
{
if (a[j - 1] == a[i])
{
x = i, y = j;
ans++;
p = a[y];
y--;
pp(p);
if (ans != -1)
{
q = ans;
}
ans = 0;
}
if (a[i + 1] == a[j])
{
x = i, y = j;
ans++;
p = a[x];
x++;
pp(p);
if (q != 0)
{
if(ans!=-1)
q = min(q, ans);
}
else
{
if (ans != -1)
{
q = ans;
}
}
ans = 0;
}
if (a[i] == a[i + 1])
{
x = i; y = j;
ans++;
p = a[x];
x++;
pp(p);
if (q != 0)
{
if(ans!=-1)
q = min(q, ans);
}
else
{
if (ans != -1)
{
q = ans;
}
}
ans = 0;
}
if (a[j] == a[j - 1])
{
x = i, y = j;
ans++;
p = a[y];
y--;
pp(p);
if (q != 0)
{
if(ans!=-1)
q = min(q, ans);
}
else
{
if (ans != -1)
{
q = ans;
}
}
}
if (q == 0)
q = -1;
break;
}
}
cout << q << '\n';
}
return 0;
}