第二次
第二次模拟考的算法考点预告:并查集、BFS、Tarjan、动态规划之斐波那契数列。
1.P1012 拼数
A 拼数 https://www.luogu.com.cn/problem/P1012
分析:此题最关键的是cmp函数,有点难想
方法一
考点:冒泡排序,string字符串类型的使用
#include<stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
using namespace std;
/*---1-----*/
string a[20];
bool cmp(string a,string b){
if(a+b>b+a) return true;
return false;
}
void swap(string &a,string &b){
string temp=a;
a=b;
b=temp;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n-1;i++){
for(int j=0;j<n-1-i;j++){
if(cmp(a[j],a[j+1])==false){
swap(a[j],a[j+1]);
}
}
}
for(int i=0;i<n;i++) cout<<a[i];
}
方法二
代码来源
使用sort方法,只需写一个cmp函数
#include<iostream>
#include<string>
#include<algorithm>//提供sort
using namespace std;
string s[25];//不多说
int n;//限制数字个数
bool cmp(string a,string b) {
return a+b>b+a;//自定义排序函数,这一步非常巧妙,假设a=321,b=32;a+b=32132,b+a=32321这样下面sort排下来就是32>321避免出现32132>32321的情况
}
/*如果这样写:
bool cmp(string a,string b){
return a>b;
会发生321>32的情况,具体原因是字符串自己的关系运算是这样设定的
}*/
int main() {
cin>>n;
for(int i=1; i<=n; i++) cin>>s[i];
sort(s+1,s+n+1,cmp);//神来之笔
for(int i=1; i<=n; i++) cout<<s[i];//完美收场(yo)!
return 0;
}
2.P1308
B 统计单词数 https://www.luogu.com.cn/problem/P1308
#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<algorithm>
#include<cctype>
using namespace std;
/*------2------*/
bool equal(char a[],char b[]){
if(strlen(a)!=strlen(b)) return false;
for(int i=0;i<strlen(a);i++){
if(a[i]!=b[i]) return false;
}
return true;
}
void change(char *a){
int len =strlen(a);
for(int i=0;i<len;i++){
if(isupper(a[i])) a[i]=a[i]+32;
}
}
int main(){
// freopen("in.txt","r",stdin);
char a[100],b[1000],temp[100],c;
scanf("%s",&a);
change(a);
int lena=strlen(a);
getchar();
int lenb=0,temp_len=0,ans=0,loc=-1;
while((c=getchar())!=EOF){
if(c=='\n') break;
if(isupper(c)) c=c+32;
lenb++;
if(c==' '){
temp[temp_len]='\0';
// printf("%s-------%s",a,temp);
if(equal(a,temp)) {
ans++;
if(loc==-1){
loc=lenb-temp_len-1;
}
}
temp_len=0;
}
else{
temp[temp_len]=c;
```cpp
temp_len++;
}
}
//如果最后一个单词temp是a(因为没有空格所以没有被判断)
if(equal(a,temp)) ans++;
if(loc==-1) printf("-1");
else printf("%d %d",ans,loc);
// fclose(stdin);
}
注意
while((c=getchar())!=EOF){
//下面句也可以不要
if(c=='\n') break;
这种写法既可以文件输入,也可以cmd中输入,都正确
但下面这种
while((c=getchar())!='\n'){
}
这种写法只能cmd命令行读取,因此这种写法读取文件时,文件结尾是-1(宏定义中的EOF值),并不是换行符!且这种写法,代码机器判别结果是RE(runtime error)超时,可知测试集也没有 ’ \n ’
方法二
<cctype>
的isupper()函数
<string.h>
的strstr(str1,str2)函数,返回str1中第一次出现str2的位置(从0开始),
定义:strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
<stdio.h>
中的gets()函数,
gets()遇换行结束,并且自动丢弃换行符,可吸收空格。
#include <cstring>
#include <cctype>
#include <cstdio>
void strlower (char *a) {//手写函数,将大写字母转换成小写字母
for(int i = 0; a[i]; i ++ ) {
if(isupper(a[i])) a[i] = tolower(a[i]);//isupper是判断是否是大写字母的系统函数,tolower是将其转换成小写字母的函数
}
}
int main () {
char destination[1000001], *q, source[11], *p;//destintion是要找的文章,source是要找的单词,p和q都是指针类,分别代表当前搜索到什么地方了和最后一次找到单词的指针
bool flag = false;//判断是否找到了
int ans = 0, ans1 = -1;//个数和首次出现的位置,ans1的初值是-1是因为在没找到的时候就直接输出就行了,省事
gets(source);
gets(destination);//输入
strlower(destination);//全部转换成小写字母
strlower(source);
int len = strlen(source);//长度,在后面防止越界和加快速度
p = destination;//先将指针设为全部
for(; q = strstr(p, source); ) {//循环,strstr是在一个字符串里面给定一个字符串,寻找有没有这个字符串,若有,返回首次出现的指针否则返回NULL(空指针)
if( q != NULL//找到了
&& ( q == destination || *(q - 1) == ' ') //第一个条件是防止越界,第二个是判断前一个是不是空格
&& ( *(q + len) == '\0' || *(q + len) == ' ' ) ) {//如果后面也是空格
ans ++ ;//答案加一
if(flag == false) {//如果是首次找到
flag = true;
ans1 = q - destination;//第一个位置
}
}
p = q + len+1;//刷新指针
}
if(flag == true)//找到了
printf("%d %d" , ans, ans1);//输出
else
printf("%d", ans1);//输出-1
return 0;
}
C 01迷宫 https://www.luogu.com.cn/problem/P1141
方法一:
自己的代码,三个点超时,和第二个代码相比,主要是每次检测新的点都会重新DFS,没有利用之前的搜索结果。其实在同一个连通分量里面的点可到达的点的数量是相同的。
const int maxn=10000;
bool inq[maxn][maxn]={false};
int matrix[maxn][maxn];
int ans,m,n;
char num[10000];
bool judge(int x,int y,int a){
if(x<0||y<0||x>=n||y>=n) return false;
if(inq[x][y]==true||matrix[x][y]==a) return false;
return true;
}
int X[4]={0,0,-1,1};
int Y[4]={1,-1,0,0};
void DFS(int x,int y){
for(int i=0;i<4;i++){
int newx=x+X[i];
int newy=y+Y[i];
int a=matrix[x][y];
if(judge(newx,newy,a)){
inq[newx][newy]=true;
ans++;
// printf("(x,y):%d,%d",newx,newy);
DFS(newx,newy);
}
}
}
int main(){
scanf("%d%d\n",&n,&m);
for(int i=0;i<n;i++){
// scanf("%d",&matrix[i][j]);
// scanf("%s",&num);
// getchar();
gets(num);
// printf("====%s****",num);
for(int j=0;j<n;j++){
matrix[i][j]=num[j]-'0';
}
}
while(m--){
int x,y;
scanf("%d %d",&x,&y);
inq[x-1][y-1]=true;
ans=1;
DFS(x-1,y-1);
printf("%d\n",ans);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
inq[i][j]=false;
}
}
}
}
方法二:
代码来源
利用了同一个连通分量里的点可以访问的点数相同,利用f[r][c]记录这个点在要求m个点的ans时,曾经第i次访问过。后面第j个点,若在同一个连通分量里,就可以利用这个结果,ans[ j ] ans[ i ]= ans[ f[r][c] ]
#include<cstdio>
#include<cstring>
int n,m,ans[100002],x,y,f[1002][1002];
char s[1002][1002];
void dfs(int r,int c,int z,int lll){
if (r<0 || r>=n || c<0 || c>=n || f[r][c]!=-1 || s[r][c]-'0'!=z)return;
f[r][c]=lll;ans[lll]++;
dfs(r-1,c,!z,lll);dfs(r+1,c,!z,lll);dfs(r,c-1,!z,lll);dfs(r,c+1,!z,lll);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
scanf("%s",s[i]);
memset(f,-1,sizeof(f));
for (int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);x--;y--;
if (f[x][y]==-1)dfs(x,y,s[x][y]-'0',i);else ans[i]=ans[f[x][y]];
}
for (int i=0;i<m;i++)
printf("%d\n",ans[i]);
return 0;
}
D 台阶问题 https://www.luogu.com.cn/problem/P1192
方法一:
利用了借鉴的数学方法。
代码借鉴
以下自己的递归方法80分,缺点是递归层数太高时,不能得到结果。例如
#include<string.h>
#include<cstdio>
int N,K;
int mod=100003,a[1000000];
int f(int n){
//如果到第n个台阶的方法总数之前计算过
if(a[n]!=-1) return a[n];
if(n<=K) a[n]=2*f(n-1)%mod;
else a[n]=(2*f(n-1)-f(n-K-1))%mod;
return a[n];
}
int main(){
memset(a,-1,sizeof(a));
a[0]=a[1]=1;
scanf("%d %d",&N,&K);
printf("%d",f(N));
}
方法二
分析:从前往后计算,每一个阶梯的方法,都是t[i-1]+…+t[i-k],且需判断减后i-j>=0
#include <bits/stdc++.h>
using namespace std;
int main()
{
int i,j,n,k;
int t[100009];
memset(t,0,sizeof(t));
cin>>n>>k;
t[0]=1;
for(i=1;i<=n;i++)
{
for(j=1;j<=k;j++)
{
if(i>=j)
{
t[i]+=t[i-j];
t[i]=t[i]%100003;
}
}
}
cout<<t[n];
return 0;
}
E 修复公路 https://www.luogu.com.cn/problem/P1111
考点:并查集
const int maxn=1010;
int n,m,father[maxn],num,ans=-1;
bool isBoot[maxn];
struct info{
int a,b,t;
}in[100000];
bool cmp(info a,info b){
return a.t<b.t;
}
int findFather(int x){
int a=x;
while(x!=father[x]){
x=father[x];
}
//此时x是根节点
while(a!=father[a]){
int z=a;
a=father[a];
father[z]=x;
}
}
void init(int n){
//n个村庄初始化
for(int i=1;i<=n;i++){
father[i]=i;
isBoot[i]=false;
}
//集合个数是n;
num=n;
}
void Union(int a,int b){
int fa=findFather(a);
int fb=findFather(b);
if(fa!=fb){
father[fa]=fb;
num--;
}
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
init(n);
for(int i=0;i<m;i++){
scanf("%d%d%d",&in[i].a,&in[i].b,&in[i].t);
}
sort(in,in+m,cmp);
for(int i=0;i<m;i++){
int A=in[i].a;
int B=in[i].b;
Union(A,B);
if(num==1){
ans=in[i].t;
break;
}
}
printf("%d",ans);
// fclose(stdin);
}
F 炸铁路 https://www.luogu.com.cn/problem/P1656
考点:tarjon/SPFA
不会