202 路径计数
有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。
一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。
由于答案很大,输出对1e9+7取模的结果。
输入格式
第一行一个正整数n。
接下来n行,每行n个正整数,1表示可以通行,0表示不能通行。
输出格式
一个整数,表示答案。
样例输入
3
1 1 1
1 0 1
1 1 1
样例输出
2
数据规模
对于100%的数据,保证2≤n≤100,左上角右下角都是可以通行的。
思路:
采用动态规划思想,设f[i][j]表示到达( i , j ) 可能的步数。
对于一点( i , j ),f[ i ][ j ]=f[ i - 1][ j ]+f[ i ][ j - 1]。
初始化时,第一行有障碍时,这一行后面的所有都应为0,列同理。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
const int p=1000000007;
ll f[110][110];
int a[110][110];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;++i)
{
if(a[1][i]==0) break;
f[1][i]=1;
}
for(int i=1;i<=n;++i)
{
if(a[i][1]==0) break;
f[i][1]=1;
}
for(int i=2;i<=n;++i)
{
for(int j=2;j<=n;++j)
{
if(a[i][j]!=0) f[i][j]=(f[i-1][j]+f[i][j-1])%(p);
}
}
printf("%lld",f[n][n]);
return 0;
}
203 最大和上升子序列
给定一个长度为 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≤1e5。
思路:
f[ i ]表示以 i 结尾的最大上升子序列和。
初始时f[ i ]最小为a[ i ],对于之前的各个f[ j ],如果f[ j ]加上当前的a[ i ] > f[ i ],并且满足递增,即a[ j ] < a[ i ],那么更新f[i]。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1010];
int f[1010];//以i结尾的最大
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
int ans=-1;
for(int i=1;i<=n;++i)
{
f[i]=a[i];
for(int j=1;j<i;++j)//j~i
{
if(a[j]<a[i])
f[i]=max(f[j]+a[i],f[i]);
}
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}
204 加一
给定一个整数 n。你需要对它做 m 次操作。在一次操作中,你要将这个数的每一位 d 替换成 d+1。比如,1912 在进行一次操作后将变成 21023。
请求出整数 n 进行了 m 次操作后的长度。答案可能很大,输出对 1e9+7 取模后的结果。
输入格式
第一行一个整数 t,表示测试单元的个数。
接下来 t 行,每行有两个整数 n 和 m,表示最初的数字和进行多少次操作。
输出格式
对于每个测试单元输出最终数字的长度,答案对 1e9+7 取模。
样例输入
5
1912 1
5 6
999 1
88 2
12 100
样例输出
5
2
6
4
2115
数据规模
所有数据保证 1≤t≤2⋅1e5,1≤n≤1e9,1≤m≤2⋅1e5。
思路:
先预处理出每一位数变换m次后能产生的位数,询问时将每一位对应的值加和输出。
设数字 ' i ' 进行 j 次可以产生f[ i ][ j ]位数 。
每一次 +1 ,j 满足:
0~8:f[ j ][ i ]=f[ j+1 ][ i-1 ];
9:f[ 9 ][ i ]=f[ 0 ][ i-1 ]+f[ 1 ][ i-1 ];
代码:
#include<bits/stdc++.h>
#include<cstring>
using namespace std;
const int p=1000000007;
int f[20][200100];//f[i][j]数字'i'进行j次可以产生f[i][j]位数
int main()
{
int T;scanf("%d",&T);
for(int i=0;i<=9;++i) f[i][0]=1;
for(int i=1;i<=200010;++i)
{
for(int j=1;j<=9;++j)
{
f[j-1][i]=f[j][i-1];
}
f[9][i]=(f[0][i-1]+f[1][i-1])%p;
}
//dp
while(T--)
{
char a[100];
int ans=0;
int k;scanf("%s %d",&a,&k);
for(int i=0;i<strlen(a);++i)
{
ans=(ans+f[a[i]-'0'][k])%p;
}
printf("%d\n",ans);
}
return 0;
}
205 跳跳
平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:
- 刚开始,派派已经位于某传送阵之上;
- 如果派派掌握一种魔法 (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) 传送至 (4,5) 以及 (1,4) 、从 (4,5) 传送至 (1,1) 以及 (1,4) 、从 (1,4) 传送至 (1,1) 以及 (4,5) 。
注意你不能使用 (0,3)+(3,1) 的魔法从 (1,1) 到达 (4,5)。因为每次移动,你只能使用一种魔法。
当然,你可以学习 (0,1),那样的话,从 (1,1) 到达 (1,4) 则需要使用 3 次 (0,1) 魔法了。
样例2输入
3
1 1
2 2
1000000000 1000000000
样例2输出
2
数据规模
- N∈[10,500]
- Xi,Yi∈[0,1e9], 但保证坐标之间两两不同。
思路:
对于在斜率相同的直线上的点,都可以用一个与此直线斜率相同的魔法跳到。
但是沿直线向上或向下的魔法阵正负并不相同,结果应乘二。
斜率不存在的情况单独考虑。
代码:
#include<bits/stdc++.h>
using namespace std;
struct node
{
int a,b;
}k[510];
double s[200010];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&k[i].a,&k[i].b);
}
int ans=1;
int cnt=0;
for(int i=2;i<=n;++i)
{
for(int j=1;j<i;++j)
{
if(k[i].b==k[j].b){ans=2;continue;}
double y=1.0*(k[i].a-k[j].a)/(k[i].b-k[j].b);
s[++cnt]=y;
}
}
sort(s+1,s+cnt+1);
for(int i=2;i<=cnt;++i)
{
if(s[i-1]!=s[i])
++ans;
}
printf("%d",ans*2);
return 0;
}
/*
算斜率互等,则共用一个即可
能去,能回来 ans*=2
*/
206 异或和或
对于一个长度为 n 的01序列 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。
给定两个01序列 s,t , 请你判断是否可以通过有限次(可以为0次)操作将序列 s 变为 t。
输入格式
第一行一个整数 t , 表示数据的组数(1≤t≤1e3)。接下来 t 组数据:
每组第一行一个01字符串 s(1≤|s|≤1e3),每组第二行一个01字符串 t(1≤|t|≤1e3)。
注意:|s| 可能不等于 |t|。
输出格式
如果可以通过有限次(可以为0次)操作将序列 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 显然无法满足要求。
思路:
异或:1^1=0;1^0=1;0^0=0;
或:1|1=1; 1|0=1;0|0=0;
这三列对应着转换的三种情况:
第一种:1^1=0,1|1=1;表示序列有两个 1 ,那么可以将其中一个转化为 0。(消1)
第二种:1^0=1,1|0=1;说明只要序列中有 1有 0,那么序列中的 0 就可以转换为1。(变1)
第三种情况无变化。
第一二条共同运用可以交换1 0的位置。
总结为:含 1 的序列可以变为其他任意的含 1 序列。
但是,全 0 序列并不能变出 1;含 1 序列也不能全部消掉 1。
得出结论,当 s , t 一个含 1,一个不含 1 时,序列不能转化;其他情况都可以互相转化。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
scanf("%d",&t);getchar();
while(t--)
{
char c;
int f1=0,f2=0,cnt1=0,cnt2=0;
//cnt统计长度
while((c=getchar())!='\n') {++cnt1;if(c=='1') f1=1;}
while((c=getchar())!='\n') {++cnt2;if(c=='1') f2=1;}
if((f1||f2)&&!(f1&&f2)||cnt1!=cnt2) printf("NO\n");
else printf("YES\n");
}
return 0;
}
207 01序列
我们称一个字符串为好字符串,指这个字符串中只包含0
和1
。
现在有一个好字符串,求这个字符串中 '1
' 恰好出现k次的子串有多少个。
输入格式
第一行给出一个数字k,表示子串中1
的个数。
第二行给出好字符串。
输出格式
输出一个整数,表示好字符串中有多少个符合条件的子串
数据范围
0≤k≤1e6, |s|≤1e6
样例输入1
1
1010
样例输出1
6
样例输入2
2
01010
样例输出2
4
思路:
以序列中的1分段,a[ i ]统计第 i 个 1 前 0 的个数,最后的 1 之后的 0 存储在a[n+1]中(n个1)。
包含 k 个 1 的字串字串个数=子串两端的 1 前后的 0 的个数+1的乘积
例如:对于01串:001000的含一个1的字串数目为(2+1)*(3+1)。
从前往后统计包含k个1的子串前后0的个数,+1、相乘、再求和即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long int
ll a[5000100];
string s;
ll ans=0;
int main()
{
int k;scanf("%d",&k);
cin>>s;
int n=s.size();
int sum=0,cnt=0;;
for(int i=0;i<n;++i)
{
if(s[i]=='1')
{
a[++cnt]=sum;//第i个'1'前有几个'0'
sum=0;
}
else ++sum;
}
a[++cnt]=sum;
if(k==0)
{
for(int i=1;i<=cnt;++i)
ans+=(a[i]+1)*a[i]/2;
}
else
for(int i=k;i<=cnt-1;++i)
{
ll l=i-k+1,r=i+1;
ans+=(a[l]+1)*(a[r]+1);
}
printf("%lld",ans);
return 0;
}
301 出栈序列判断
现在有一个栈,有 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,输入一定是个合法的出栈序列。
思路:
出栈的一定是栈的顶层。由于是从小到大入栈的,设此次将出栈的小于最后一个已经出栈的,说明在此期间没有入栈;大于最后一个已经出栈的那说名直到此次出栈的数为止的所有数已经入栈了。由此模拟。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1000100];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
int top=1;
for(int i=1;i<=n;++i)
{
if(a[i]<a[i-1])
{
printf("pop\n");
continue;
}
for(int j=top;j<=a[i];++j)
{
printf("push %d\n",j);
}
printf("pop\n");
top=a[i]+1;
}
return 0;
}
/*
从push开始,
第一个值i是第一次pop
如果下一个值j小于 i则pop直到j>i
从i+1开始push,k是第一次pop
*/
302 序列维护
你有一个序列,现在你要支持几种操作:
-
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≤1e3。
对于insert操作,保证1≤y≤1e9。
对于所有操作,保证位置不会超出当前序列的长度。
思路:
vector数组可以直接在某位插入、删除一个数,查找时下标查询即可。
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> l;
int main()
{
int T;scanf("%d",&T);
while(T--)
{
string s;
int x,y;
cin>>s;
if(s=="insert")
{
scanf("%d%d",&x,&y);
l.insert(l.begin()+x,y);
}
else if(s=="delete")
{
scanf("%d",&x);
l.erase(l.begin()+x-1);
}
else if(s=="query")
{
scanf("%d",&x);
printf("%d\n",l[x-1]);
}
}
return 0;
}
303 网格判断
您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:
-
每行的黑色方块数与白色方块数相同。
-
每列的黑色正方形数与白色方块数相同。
-
没有行或列具有 3 个及以上相同颜色的连续正方形。
给定网格,确定它是否正确。
输入格式
第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。
接下来 n 行,每行包含一个长度为n的由字符B
和W
组成的字符串,代表网格正方形的颜色。
输出格式
如果网格正确,请打印数字 1 在一行上。否则,请打印数字 0 在一行上。
样例输入
4
WBBW
WBWB
BWWB
BWBW
样例输出
1
思路:直接模拟
先循环行列判断是否行列中B和W数量相等,再
代码:
#include<bits/stdc++.h>
using namespace std;
int a[30][30];
int f=1;
int main()
{
int n;scanf("%d",&n);
char c;
for(int i=1;i<=n;++i)
{
getchar();
for(int j=1;j<=n;++j)
{
scanf("%c",&c);
if(c=='B') a[i][j]=1;
}
}
for(int i=1;i<=n;++i)
{
int hang=0,lie=0;
for(int j=1;j<=n;++j)
{
if(a[i][j]) hang++;
if(a[j][i]) lie++;
if(j>=3)
{
if(a[i][j-2]==a[i][j-1]&&a[i][j-1]==a[i][j]) f=0;
if(a[j-2][i]==a[j-1][i]&&a[j-1][i]==a[j][i]) f=0;
}
}
if(hang*2==n&&lie*2==n)
{
continue;
}
f=0;
}
printf("%d",f);
return 0;
}
304 整齐的数组
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 是偶数),−1e6≤ai≤1e6,并且 n 的总和不超过100。
思路:如果每个数都减去最小值,那么需要满足的条件转化为求除0外的所有a[ i ]的最大公约数。如果最大公约数是0,则k为无穷大,输出-1。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[100];
int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
int k=1e9;
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
k=min(k,a[i]);
}
int ans=0;
for(int i=1;i<=n;++i)
{
if(k!=a[i]);
ans=gcd(ans,a[i]-k);//ans=0时gcd=a[i]-k
}
if(ans==0) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
/*
*/