T1.路径计数
有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。
一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。
由于答案很大,输出对10^9+7取模的结果。
输入格式
第一行一个正整数n。
接下来n行,每行n个正整数,1表示可以通行,0表示不能通行。
输出格式
一个整数,表示答案。
样例输入
3
1 1 1
1 0 1
1 1 1
样例输出
2
数据规模
对于100%100%的数据,保证2≤n≤100,左上角右下角都是可以通行的。
一道DP题,对于每个不是障碍的格子,能走到这个格子的情况数为走到上面格子的情况数加上走到左面格子的情况数,若上面或左面为障碍或边界,则按0计算,最后得到的dp[i][j]即为情况数
#include <iostream>
using namespace std;
int main(){
int map[105][105] = {0};
int n;
cin >> n;
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++){
cin >> map[i][j];
if(map[i][j]){
if(i==0&&j==0);
else if(i==0)map[i][j] = map[i][j-1];
else if(j==0)map[i][j] = map[i-1][j];
else map[i][j] = (map[i-1][j] + map[i][j-1])%(1000000007);
}
}
}
cout << map[n-1][n-1];
return 0;
}
T2.最大和上升子序列
给定一个长度为 n 的数组 a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n 并且 ap1<ap2<⋯<apm,使得ap1+ap2+⋯+apm最大。
输入格式
第一行一个数字 n。
接下来一行 n 个整数 a1,a2,…,an。
输出格式
一个数,表示答案。
样例输入
6
3 7 4 2 6 8
样例输出
21
数据规模
所有数据保证 1≤n≤1000,1≤ai≤10^5。
也是一道动态规划题,对于每一个ai,从头开始一直遍历到ai-1,寻找比他小的数,计算这个数后接ai的最大值,转移方程f[i] = max(f[j]+ai,f[i]),同时维护一个最大值,最后输出即可
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int a[1005] = {0};
int dp[1005] = {0};
for(int i = 0;i < n;i++){
cin >> a[i];
}
for(int i = 0;i < n;i++){
dp[i] = a[i];
for(int j = 0;j < i;j++){
if(a[j]<a[i]){
dp[i] = max(dp[i],dp[j]+a[i]);
}
}
}
cout << dp[n-1];
return 0;
}
T3.加一
给定一个整数 n�。你需要对它做 m� 次操作。在一次操作中,你要将这个数的每一位 d� 替换成 d+1�+1。比如,19121912 在进行一次操作后将变成 2102321023。
请求出整数 n� 进行了 m� 次操作后的长度。答案可能很大,输出对 109+7109+7 取模后的结果。
输入格式
第一行一个整数 t,表示测试单元的个数。
接下来 t 行,每行有两个整数 n 和 m,表示最初的数字和进行多少次操作。
输出格式
对于每个测试单元输出最终数字的长度,答案对 10^9+7 取模。
样例输入
5
1912 1
5 6
999 1
88 2
12 100
样例输出
5
2
6
4
2115
数据规模
所有数据保证 1≤t≤2⋅10^5,1≤n≤10^9,1≤m≤2⋅10^5。
首先不难发现这道题对于每一个数字都是分开计算的,假设要求114加514次之后的位数,我们只需要知道1加514次之后的位数和4加514次之后的位数,然后让他们依次相加即可,用f[i][j]表示 i 加 j 次之后的位数,发现在十次之后,有方程 f [ i ][ j ] = f [ i ][ j-9 ] + f [ i ][ j-10 ],所以对于前十次直接模拟,后面用转移方程求出位数,预处理完毕后只需要读入数字进行相应位数的相加即可输出答案,代码如下
#include <iostream>
using namespace std;
#define mod 1000000007
int dp[10][200005] = {0};
int main() {
int t,n,m,ans;
for(int i = 0;i < 10;i++){
for(int j = 0;j < 10;j++){
if(i + j < 10)dp[i][j] = 1;
else dp[i][j] = 2;
}
}
for(int i = 0;i < 10;i++){
for(int j = 10;j <= 200000;j++){
dp[i][j] = (dp[i][j-9]+dp[i][j-10])%mod;
}
}
cin >> t;
while(t--){
ans = 0;
scanf("%d %d",&n,&m);
while(n){
ans = (ans+dp[n%10][m])%mod;
n /= 10;
}
printf("%d\n",ans);
}
return 0;
}
T4.跳跳
平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:
- 刚开始,派派已经位于某传送阵之上;
- 如果派派掌握一种魔法 (A,B),其中 A,B 均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y) 瞬间移动至 (X+A,Y+B);
- 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法。
问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?
输入格式
第一行一个整数 N。
接下来一行 N 行,每行包含两个整数 Xi,Yi, 表示每个魔法阵的坐标。
输出格式
一个数,表示答案。
样例1输入
3
1 1
4 5
1 4
样例1输出
6
解释: 任务是从 (1,1)(1,1) 传送至 (4,5)(4,5) 以及 (1,4)(1,4) 、从 (4,5)(4,5) 传送至 (1,1)(1,1) 以及 (1,4)(1,4) 、从 (1,4)(1,4) 传送至 (1,1)(1,1) 以及 (4,5)(4,5) 。
注意你不能使用 (0,3)+(3,1)(0,3)+(3,1) 的魔法从 (1,1)(1,1) 到达 (4,5)(4,5)。因为每次移动,你只能使用一种魔法。
当然,你可以学习 (0,1)(0,1),那样的话,从 (1,1)(1,1) 到达 (1,4)(1,4) 则需要使用 33 次 (0,1)(0,1) 魔法了。
样例2输入
3
1 1
2 2
1000000000 1000000000
样例2输出
2
数据规模
- N∈[10,500]
- Xi,Yi∈[0,109], 但保证坐标之间两两不同。
对于任意三个传送阵,如果他们位于同一斜率上,那只需要学两种魔法就可以在它们之间任意传送(往返两种),所以这道题我们只需要枚举所有传送阵的两两组合,求出有多少种斜率,斜率的种数即学习传送魔法的个数,代码如下
#include <iostream>
#include <map>
using namespace std;
int main(){
int N,a,b,ans=0;
int magic[2][505];
double k;
map<double,int>ma;
scanf("%d",&N);
for(int i = 0;i < N;i++){
scanf("%d %d",&magic[0][i],&magic[1][i]);
}
for(int i = 0;i < N;i++){
for(int j = 0;j < i;j++){
a = magic[0][i] - magic[0][j];
b = magic[1][i] - magic[1][j];
k = 1.0*b/a;
if(ma.find(k)==ma.end()){
ans+=2;
ma[k] = 0;
}
}
}
printf("%d",ans);
return 0;
}
T5.异或和或
对于一个长度为 n 的0101序列 a1,a2,…,an。
你可以执行以下操作任意多次:
-
选择两个下标 1≤i,j≤n(i≠j)。
-
记x=ai xor aj , y=ai or aj , 其中 xorxor 表示按位异或 , oror 表示按位或。
-
然后令 ai=x,aj=y 或 ai=y,aj=x。
给定两个0101序列 s,t , 请你判断是否可以通过有限次(可以为00次)操作将序列 s 变为 t。
输入格式
第一行一个整数 t , 表示数据的组数(1≤t≤10^3)。接下来 t 组数据:
每组第一行一个0101字符串 s(1≤|s|≤10^3),每组第二行一个01字符串 t(1≤|t|≤10^3)。
注意:|s| 可能不等于 |t|。
输出格式
如果可以通过有限次(可以为00次)操作将序列 s 变为 t , 输出 YES
, 否则输出 NO
。
样例输入
2
001
011
11
101
样例输出
YES
NO
样例解释
第一组数据选择 i=2,j=3 , 那么 x=1,y=1 , 接着令 ai=x,aj=y 即可得到 t 序列。
第二组数据 |s|=2,|t|=3 显然无法满足要求。
根据题目,我们可以发现数字之间的转换分下面几种情况
11 -> 10 or 01
01 or 10 -> 11
00 -> 00;
不难得到,只要一个字符串中带1,它可以在任意同样长度且也有1的字符串之间转换,如果字符串中只有0,它无法进行任何有效的转换,只能变为它本身,分情况讨论即可
#include <iostream>
using namespace std;
int main(){
int t,s0,s1,t0,t1;
char ch;
scanf("%d",&t);
getchar();
while(t--){
s0 = 0;
s1 = 0;
t0 = 0;
t1 = 0;
while((ch = getchar()) != '\n'){
if(ch == '1')s1++;
else s0++;
}
while((ch = getchar()) != '\n'){
if(ch == '1')t1++;
else t0++;
}
if((s0+s1)!=(t0+t1))printf("NO\n");
else if(s1>0&&t1>0)printf("YES\n");
else if(s0==t0)printf("YES\n");
else printf("NO\n");
}
return 0;
}
T6.01序列
我们称一个字符串为好字符串,指这个字符串中只包含0
和1
。
现在有一个好字符串,求这个字符串中1
恰好出现k次的子串有多少个。
输入格式
第一行给出一个数字k,表示子串中1
的个数。
第二行给出好字符串。
输出格式
输出一个整数,表示好字符串中有多少个符合条件的子串
数据范围
0≤k≤10^6, |s|≤10^6
样例输入1
1
1010
样例输出1
6
样例输入2
2
01010
样例输出2
4
对于字符串010010100,它包含两个1的字串有13个,分两种情况讨论,一是包括第一第二个1的字串,它有2*2 = 4种,即左边一个0有两种情况,右边一个0也有两种情况,所以总共四种情况,同理包括右边两个1的字串有3*3=9种,我们只需要记下1出现的位置和字符串的长度,即可得出没两个1之间的零的个数,之后对于每一种包含了不同1的字串分别计算并相加即可得出答案
#include <iostream>
#include <cmath>
using namespace std;
long long loc[1000005] = {0};
int main(){
int k;
long long ans = 0;
int cnt = 0;
int len = 0;
char ch;
scanf("%d",&k);
getchar();
while((ch = getchar()) != '\n'){
len++;
if(ch == '1'){
loc[++cnt] = len;
}
}
loc[cnt+1] = len + 1;
if(k){
for(int i = 1;i + k - 1 <= cnt;i++){
ans += (loc[i] - loc[i-1]) * (loc[i + k] - loc[i + k - 1]);
}
}
else{
for(int i = 1;i <= cnt+1;i++){
ans += ((loc[i] - loc[i-1] - 1)*(loc[i] - loc[i-1]))/2;
}
}
cout << ans << endl;
return 0;
}
T7.出栈序列判断
现在有一个栈,有 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
操作,可以证明一个出栈序列对应的操作序列是唯一的。
样例输入1
3
2 3 1
样例输出1
push 1
push 2
pop
push 3
pop
pop
样例输入2
5
1 3 5 4 2
样例输出2
push 1
pop
push 2
push 3
pop
push 4
push 5
pop
pop
pop
数据规模
对于 100% 的数据,保证 1≤n≤100000,输入一定是个合法的出栈序列。
模拟一个栈进行操作,使它的出栈序列为特定的,同时规定好了入栈的顺序,因为只有两种操作,我们可以先写一个栈,若栈顶不是下一个要出栈的数字,就按顺序进行入栈操作,直到栈顶变为下一个要出栈的数字,之后进行pop操作并继续判断下一个数字是否为下一个要出栈的数字,若是继续pop,若不是就push,直到全部处理完毕即可
#include <iostream>
#include <stack>
using namespace std;
int sta[100005] = {0};
int main(){
int n;
int p = 0;
int cnt = 2;
stack<int>st;
cin >> n;
for(int i = 0;i < n;i++){
scanf("%d",&sta[i]);
}
st.push(1);
printf("push 1\n");
while(p!=n||!st.empty()){
while(st.empty()||(st.top()!=sta[p])){
st.push(cnt);
printf("push %d\n",cnt++);
}
while(!st.empty()&&st.top()==sta[p]){
st.pop();
p++;
printf("pop\n");
}
}
return 0;
}
T8.序列维护
你有一个序列,现在你要支持几种操作:
-
insert x y
,在从前往后的第x个元素后面插入y这个数。如果x=0,那么就在开头插入。 -
delete x
,删除从前往后的第x个元素。 -
query k
,询问从前往后数第k个元素是多少。
输入格式
第一行一个整数m,表示操作个数。
接下来m行,每行一个上面所述的操作。
输出格式
输出若干行,对于每个查询操作,输出答案。
样例输入
10
insert 0 1
insert 1 2
query 1
query 2
insert 0 3
query 1
delete 1
query 1
insert 1 4
query 2
样例输出
1
2
3
1
4
数据规模
对于100%的数据,保证m≤10^3。
对于insert操作,保证1≤y≤10^9。
对于所有操作,保证位置不会超出当前序列的长度。
简单的维护一个序列,所有的操作都可以用vector实现,只需要按照输入进行调用即可
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n,a,b;
vector<int>v;
string s;
cin >> n;
while(n--){
cin >> s;
if(s == "insert"){
scanf("%d %d",&a,&b);
v.insert(v.begin()+a,b);
}else if(s == "delete"){
scanf("%d",&a);
v.erase(v.begin()+a-1);
}else{
scanf("%d",&a);
printf("%d\n",v[a-1]);
}
}
return 0;
}
T9.网格判断
您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:
-
每行的黑色方块数与白色方块数相同。
-
每列的黑色正方形数与白色方块数相同。
-
没有行或列具有 3 个及以上相同颜色的连续正方形。
给定网格,确定它是否正确。
输入格式
第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。
接下来 n 行,每行包含一个长度为n的由字符B
和W
组成的字符串,代表网格正方形的颜色。
输出格式
如果网格正确,请打印数字 1 在一行上。否则,请打印数字 0 在一行上。
样例输入
4
WBBW
WBWB
BWWB
BWBW
样例输出
1
按照题目意思进行逐个判断即可,注意条件不要写错
#include <iostream>
using namespace std;
int main(){
int n,cntb,cntw;
char m[25][25];
bool back,th,is = true;
cin >> n;
for(int i = 0;i < n;i++){
cin >> m[i];
}
for(int i = 0;i < n;i++){
th = (m[i][0] == m[i][1]);
for(int j = 1;j < n;j++){
back = th;
th = (m[i][j] == m[i][j+1]);
if(back&&th){
is = false;
}
}
}
for(int i = 0;i < n;i++){
cntb = 0;
cntw = 0;
for(int j = 0;j < n;j++){
if(m[i][j]=='B')cntb++;
else cntw++;
}
if(cntb!=cntw){
is = false;
}
}
for(int i = 0;i < n;i++){
th = (m[0][i] == m[1][i]);
for(int j = 1;j < n;j++){
back = th;
th = (m[j][i] == m[j+1][i]);
if(back&&th){
is = false;
}
}
}
for(int i = 0;i < n;i++){
cntb = 0;
cntw = 0;
for(int j = 0;j < n;j++){
if(m[j][i]=='B')cntb++;
else cntw++;
}
if(cntb!=cntw){
is = false;
}
}
if(is)cout << 1;
else cout << 0;
return 0;
}
T10.整齐的数组
Polycarp 有一个长度为 n 的数组 a1,a2,...,an(n 是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n)使 ai 减去 k。
在 Polycarp 进行若干次操作后(可能 0 次),数组 a 中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1。
输入格式
第一行一个整数 t,表示测试单元的个数。
接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,...,an。
输出格式
对于每个测试单元输出单独一行一个整数 k (k≥1) —— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果 k 能任意大的话。
样例输入
3
6
1 5 3 1 1 5
8
-1 0 1 -1 0 1 -1 0
4
100 -1000 -1000 -1000
样例输出
2
1
1100
数据规模
所有数据保证 1≤t≤10,4≤n≤40(n 是偶数),−106≤ai≤106,并且 n 的总和不超过100。
只需要对所有差值寻找他们的最大公因数即可
#include <iostream>
#include <algorithm>
using namespace std;
int gys(int x, int y)
{
if(x<y)swap(x,y);
int z = y;
while(x%y!=0)
{
z = x%y;
x = y;
y = z;
}
return z;
}
int main(){
int t,n,a[45],m[45],cnt,temp;
cin >> t;
while(t--){
cnt = 0;
cin >> n;
for(int i = 0;i < n;i++){
cin >> a[i];
}
sort(a,a+n);
for(int i = n-1;i > 0;i--){
if(a[i]-a[i-1])m[cnt++] = a[i]-a[i-1];
}
if(cnt == 0){
cout << -1 << endl;
}else if(cnt == 1){
cout << m[0] << endl;
}else{
temp = gys(m[0],m[1]);
for(int i = 2;i < cnt;i++){
temp = gys(temp,m[i]);
}
cout << temp << endl;
}
}
return 0;
}