2023年第十四届蓝桥杯大赛软件类省赛C/C++大学B组真题

锲而不舍,金石可镂。

打卡第三篇。

A.日期统计

#include<bits.stdc++.h>
using namespace std;
 
int main() {
    int array[100] = {
        5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,
        5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,
        2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3,
        8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6,
        1, 4, 0, 1, 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
    };
 
    int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int ans = 0;
 
    for (int month = 1; month <= 12; ++month) {
        for (int day = 1; day <= daysInMonth[month]; ++day) {
            int dateSeq[8] = {2, 0, 2, 3, month / 10, month % 10, day / 10, day % 10};
            int k = 0;
 
            for (int i = 0; i < 100; ++i) {
                if (array[i] == dateSeq[k]) {
                    ++k;
                    if (k == 8) {
                        ans++;
                        break;
                    }
                }
            }
        }
    }
 
    cout<<ans<<'\n';
    return 0;
}

B.串的熵 

 

H(S) = -(0的个数 * 0的占比 * log2(0的占比)) - (1的个数 * 1的占比 * log2(1的占))

答案:11027421 。

#include <bits/stdc++.h>
using namespace std;
typedeef long long ll;
const double db = 1e-4;
const ll val = 23333333;
const double sh = 11625907.5798;
int main()
{
	ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);
		
	for (ll i = 0; i < val / 2; i++)
	{
		ll k = val - i;
		double sum = -1.0 * i * i / val * log2(1.0 * i / val) - 1.0 * k * k / val * log2(1.0 * k / val);
		if (abs(sh - sum) <= db)
		{
			cout << i << endl;
			break;
		}
	}
	return 0;
}

C.冶炼金属

 

D.飞机降落

正确代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct fly{
	ll st,ed,time;
};
ll n;
bool dfs(ll cur_time,ll cnt,vector<ll> &vis,vector<fly> &air){
	if(cnt==n){
		return true;
	}
	
	for(ll i=1;i<=n;i++){
		if(vis[i]==0){
			if(cur_time<=(air[i].st+air[i].ed)){
				vis[i]=1;
				ll start_time=max(cur_time,air[i].st);
				if(dfs(start_time+air[i].time,cnt+1,vis,air)) return true;
				vis[i]=0;
			}else continue;
		}
	}
	return false;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;
	while(t--){
		cin>>n;
		vector<fly> air(12,{0,0,0});
		vector<ll> vis(12,0);
		for(ll i=1;i<=n;i++){
			cin>>air[i].st>>air[i].ed>>air[i].time;
		}
		if(dfs(0,0,vis,air))cout<<"YES"<<'\n';
		else cout<<"NO"<<'\n';
		//air.clear();vis.clear();
	}
	return 0;
}

关于内存分配错误(错误代码)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct fly{
	ll st,ed,time;
};
ll n;
vector<fly> air(12,{0,0,0});
vector<ll> vis(12,0);
bool dfs(ll cur_time,ll cnt,vector<ll> &vis,vector<fly> &air){
	if(cnt==n){
		return true;
	}
	
	for(ll i=1;i<=n;i++){
		if(vis[i]==0){
			if(cur_time<=(air[i].st+air[i].ed)){
				vis[i]=1;
				ll start_time=max(cur_time,air[i].st);
				if(dfs(start_time+air[i].time,cnt+1,vis,air)) return true;
				vis[i]=0;
			}else continue;
		}
	}
	return false;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t;cin>>t;
	while(t--){
		cin>>n;
		for(ll i=1;i<=n;i++){
			cin>>air[i].st>>air[i].ed>>air[i].time;
		}
		if(dfs(0,0,vis,air))cout<<"YES"<<'\n';
		else cout<<"NO"<<'\n';
		air.clear();vis.clear();
	}
	return 0;
}

 

所以容器大小和容量还是有区别的,谨慎使用clear(); 

E.数字接龙 

暴力码

#include <bits/stdc++.h>
using namespace std;

// 获取数字的首位数字
int get_first_digit(int num) {
    while (num >= 10) {
        num /= 10;
    }
    return num;
}

// 获取数字的末位数字
int get_last_digit(int num) {
    return num % 10;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int N;
    cin >> N;
    vector<int> arr(N);
    for (int i = 0; i < N; ++i) {
        cin >> arr[i];
    }
    
    // dp[i] 表示以第 i 个数结尾的最长接龙子序列的长度
    vector<int> dp(N, 1);
    
    // 计算最长接龙数列
    for (int i = 1; i < N; ++i) {
        for (int j = 0; j < i; ++j) {
            // 如果 arr[j] 的末位和 arr[i] 的首位相等
            if (get_last_digit(arr[j]) == get_first_digit(arr[i])) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }
    
    // 最长接龙数列的长度
    int max_chain_len = *max_element(dp.begin(), dp.end());
    
    // 最少删除的元素数量 = 总长度 - 最长接龙数列的长度
    cout << N - max_chain_len << endl;
    
    return 0;
}

答案代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    int n, x, a, p[10] = {0};  // p[10] 是记录尾位为0到9的接龙数列长度
    scanf("%d", &n);  // 读取序列的长度
    for(int i = 0; i < n; i++){
        scanf("%d", &a);  // 读取每个数字
        x = a % 10;  // 获取当前数字的末位数字
        while(a / 10) a /= 10;  // 提取当前数字的首位数字
        if(p[a] + 1 > p[x]) p[x] = p[a] + 1;  // 更新以 x 为尾的接龙数列长度
    }
    
    // 将尾位为 0 到 9 的最大接龙数列长度传给 p[0]
    for(int i = 1; i < 10; i++) if(p[i] > p[0]) p[0] = p[i];
    
    // 最少删除的数字数目 = 总数 n - 最长接龙数列的长度
    printf("%d", n - p[0]);  
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n;cin>>n;
	vector<ll> dp(10,0);
	for(ll i=0;i<n;i++){
		string x;cin>>x;
		dp[x.back()-'0']=max(dp[x.back()-'0'],dp[x.front()-'0']+1);
		//比较的并非当前字符串,而是接龙子序列的头部和尾部
//dp[x[x.length()-1]-'0']=max(dp[x[x.length()-1]-'0'],dp[x[0]]+1);
	}
	sort(dp.rbegin(),dp.rend());
	cout<<(n-dp[0]);
	return 0;
}

F.岛屿个数

方法:

2次bfs

#include <stdio.h>  //引入库函数 输入输出

#include <string.h>// 字符串与memset

#include <stdbool.h>//bool类型

#include <stdlib.h>//malloc

char grid[55][55]; //记录数组

bool visit[55][55]; //访问状态数组

int seax[8]={0,0,-1,1,1,1,-1,-1}; //海水方向盘

int seay[8]={1,-1,0,0,-1,1,1,-1};

int roadx[4]={0,0,-1,1}; //陆地方向盘

int roady[4]={1,-1,0,0};

bool check(int i,int j,int m,int n){ //check方法

if(i<0 || j<0 || i>=m || j>=n){

return false;

}

return true;

}



void bfs_road(int x,int y,int m,int n){ //bfs遍历陆地方法 与遍历海洋思路相同,不赘述

int quene[2555][2];

int first=0;

int rear=0;

visit[x][y]=true;

quene[rear][0]=x;

quene[rear][1]=y;

rear++;

while(first<rear){

int i=quene[first][0];

int j=quene[first][1];

first++;

for(int k=0;k<4;++k){

int ni=i+roadx[k];

int nj=j+roady[k];

if(check(ni,nj,m,n) && grid[ni][nj]=='1'  && visit[ni][nj]==false){

visit[ni][nj]=true;

quene[rear][0]=ni;

quene[rear][1]=nj;

rear++;

}

}

}

}

int bfs_sea(int x,int y,int m,int n,int ans){ //bfs遍历海洋方法

int quene[2555][2];  //C语言用数组模拟队列,根据变量范围确定数量大小

int first=0; //队列首

int rear=0;//队列尾

visit[x][y]=true;//将该点设为已访问过

quene[rear][0]=x;//将该点加入队列

quene[rear][1]=y;

rear++;//队列尾自增

while(first<rear){ //当队列中还有元素

int i=quene[first][0];//抽出目前的元素

int j=quene[first][1];

first++;//队列首自增

for(int k=0;k<8;++k){//调用方向盘找旁边的元素

int ni=i+seax[k];

int nj=j+seay[k];

if(check(ni,nj,m,n) && grid[ni][nj]=='0' && visit[ni][nj]==false ){//满足两大条件 且还是海

visit[ni][nj]=true;    //将该点设为已访问,加入队列继续寻找旁边的岛屿

quene[rear][0]=ni;

            quene[rear][1]=nj;

            rear++;

}

if(check(ni,nj,m,n) && grid[ni][nj]=='1' && visit[ni][nj]==false ){//满足两大条件 且是陆地

ans++; //找到岛屿了,数量+1

bfs_road(ni,nj,m,n);//用bfs访问整个岛屿,防止重复计算

}

}

}

return ans; //返回最终得出的岛屿数量

}





int main(){ //主方法

int T;   

scanf("%d",&T); //读入T

int *ans = (int*)malloc(sizeof(int)*T); //创造一个长度为T的数组,每个元素记录该组数据有几个岛屿

for(int k=0;k<T;++k){ //对T组数据进行一一判定

    int m,n;

    scanf("%d %d",&m,&n);  //读入行列

for(int i=0;i<m;++i){  //读入地图 

scanf("%s",&grid[i]);// 这里要注意输入的是字符串,看好类型

  }  

bool flag=false; //如果整个地图外围就是一个大环,全都是陆地,flag一直是false,会返回岛屿数目为1

    memset(visit,false,sizeof(visit)); //设定全图为未访问状态

    int anss=0; //定义岛屿数目变量为0

for(int i=0;i<m;++i){ //双重for循环遍历整个地图

for(int j=0;j<n;++j){

if(i==0 || i==m-1 || j==0 || j==n-1){ //先从外面边界遍历 防止遍历里面遍历到环的内部

if(visit[i][j]==false && grid[i][j]=='0' ){//如果找到了边界的海

flag=true;  //地图不是一个大环

anss=bfs_sea(i,j,m,n,anss);//调用dfs方法返回岛屿数目

}

}

}

}

if(flag==false){  //如果整个地图外围就是一个大环,会返回岛屿数目为1

anss=1;

}

ans[k]=anss;//记录该组数据岛屿数目

}  

for(int i=0;i<T;++i){ //分行输出存储的岛屿数目数组

printf("%d\n",ans[i]);

}

return 0; //返回值

} 

bfs+dfs

#include<bits/stdc++.h>
using namespace std;
const int N = 50+10;
string a[N];//地图 
bool v[N][N];//标记是否访问 
int s,w,ans=0;//s,w地图大小 
int lx[]={1,0,-1,0};//4个方向向量 
int ly[]={0,-1,0,1};//lx,ly为岛屿探索4个方向 
int hx[]={1,0,-1,0,1,1,-1,-1} ;//hx,hy为海的8个方向 
int hy[]={0,-1,0,1,-1,1,1,-1};
bool check(int x,int y);//检查数组下标是否合理 
void bfs (int x,int y);
void dfs(int x,int y) ;
 
void bfs(int x,int y){
    if(!check(x,y)) return;//下标不合理 
    v[x][y]=true;   //标记访问 
    for(int i=0;i<8;i++){//8个方向依次探索 
        if(check(x+hx[i],y+hy[i])&&!v[x+hx[i]][y+hy[i]]){//可以访问的下一块区域 
            if(a[x+hx[i]][y+hy[i]]=='1') {//下一块区域 是岛屿 
                dfs(x+hx[i],y+hy[i]);//先标记所有与他相连的岛屿 
                ans++;//岛屿数量加1 
            }
            else {
                bfs(x+hx[i],y+hy[i]);//下一块区域 是海,深入探索 
            }
        }
    }  
}
 
void dfs(int x,int y){
    if(!check(x,y)) return ;//下标不合理 
    v[x][y] = true;  //标记访问
    for(int i=0;i<4;i++){//4个方向依次探索 
        //可以访问该方向的岛屿 
        if(check(x+lx[i],y+ly[i])&&a[x+lx[i]][y+ly[i]]=='1'&&!v[x+lx[i]][y+ly[i]]){        
            dfs(x+lx[i],y+ly[i]);//继续寻找相连岛屿 
        }
    }
}
 
bool check(int x,int y){ 
    return (x>=0&&x<s&&y>=0&&y<w);
}
 
void solve(){//读取数据 
    memset(v,false,sizeof(v));//初始或重置标记数组 
    cin>>s>>w;
    for(int i=0;i<s;i++){
        cin>>a[i];
    }
    //寻找4边的海域作为着点,注意循环条件不要重复顶点 
    for(int i=0;i<w;i++){
        if(a[0][i]=='0') {
            bfs(0,i);
        }
        if(a[s-1][i]=='0'){
            bfs(s-1,i);
        }
    }
    for(int i=1;i<s-1;i++){
        if(a[i][0]=='0') {
            bfs(i,0);
        }
        if(a[i][w-1]=='0'){
            bfs(i,w-1);
        }
    }  
    cout<<ans<<endl;
    ans =  0;
}
 
int main()
{
    int t=1;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

2次dfs(染色+合并)(Floodfill算法(泛洪填充算法))

 

 

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

// 四连通方向,用于 DFS2(陆地内部的搜索)
ll dx[] = {-1, 1, 0, 0};
ll dy[] = {0, 0, -1, 1};

// 八连通方向,用于 DFS1(水域外部的搜索)
ll sx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
ll sy[] = {0, 0, -1, 1, -1, 1, -1, 1};

ll m, n;  // 注意:这里 m 表示行数,n 表示列数

/*
  dfs1:从外部水域出发,以八连通方式搜索,将所有与外部连通的水域标记为 '2'。
  参数说明:
    st, ed —— 当前搜索点的行和列下标
    mp —— 地图,传递时必须按引用传递,才能修改原数组
    vis —— 访问标记数组,标记某个格子是否已访问过
*/
void dfs1(ll st, ll ed, vector<vector<char>> &mp, vector<vector<ll>> &vis) {
    mp[st][ed] = '2';   // 将当前水域标记为 '2'
    vis[st][ed] = 1;     // 同时标记为已访问
    for (ll i = 0; i < 8; i++) {
        ll nx = st + sx[i];
        ll ny = ed + sy[i];
        // 地图大小为 (m+2) x (n+2)(包含外围的海水)
        if (nx >= 0 && nx <= m + 1 && ny >= 0 && ny <= n + 1 &&
            !vis[nx][ny] && mp[nx][ny] == '0') {
            dfs1(nx, ny, mp, vis);
        }
    }
}

/*
  dfs2:从陆地出发,以四连通方式搜索,将整个岛屿区域标记为 '2',避免重复计数。
  参数说明:
    st, ed —— 当前搜索点的行和列下标
    mp —— 地图,按引用传递,修改原数组
    vis —— 访问标记数组
*/
void dfs2(ll st, ll ed, vector<vector<char>> &mp, vector<vector<ll>> &vis) {
    mp[st][ed] = '2';   // 标记当前陆地为 '2'
    vis[st][ed] = 1;     // 标记为已访问
    for (ll i = 0; i < 4; i++) {
        ll nx = st + dx[i];
        ll ny = ed + dy[i];
        // 陆地区域只在原地图内部,即行在 [1, m],列在 [1, n]
        if (nx >= 1 && nx <= m && ny >= 1 && ny <= n &&
            !vis[nx][ny] && mp[nx][ny] == '1') {
            dfs2(nx, ny, mp, vis);
        }
    }
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    ll t;
    cin >> t;
    while (t--) {
        // 输入地图行数 m 和列数 n(原题中 m,n 代表地图尺寸)
        cin >> m >> n;
        // 构造一个比原地图大一圈的二维数组,行数 = m+2,列数 = n+2,初始全部填充为 '0'(海水)
        vector<vector<char>> mp(m + 2, vector<char>(n + 2, '0'));
        // 构造与 mp 同尺寸的访问标记数组,初始全为 0(表示未访问)
        vector<vector<ll>> vis(m + 2, vector<ll>(n + 2, 0));
        
        // 读入实际地图数据存放在 mp 的内部区域(1 ≤ i ≤ m, 1 ≤ j ≤ n)
        for (ll i = 1; i <= m; i++) {
            for (ll j = 1; j <= n; j++) {
                cin >> mp[i][j];
            }
        }
        
        // 第一次 DFS:从外部的 (0,0) 点出发,以八连通方式将所有与外界连通的海水标记为 '2'
        dfs1(0, 0, mp, vis);
        
        // 合并岛屿内部的水域:将未被 dfs1 标记的 '0'(即岛屿内部的水)变为 '1'
        for (ll i = 1; i <= m; i++) {
            for (ll j = 1; j <= n; j++) {
                if (mp[i][j] == '0')
                    mp[i][j] = '1';
            }
        }
        
        // 在进行 DFS2 之前,重置访问标记数组 vis 为 0,
        // 因为之前 DFS1 的访问标记不能用于 DFS2
        for (ll i = 0; i < m + 2; i++) {
            for (ll j = 0; j < n + 2; j++) {
                vis[i][j] = 0;
            }
        }
        
        ll cnt = 0; // 用于计数岛屿数量
        // 第二次 DFS:遍历内部区域(1 ≤ i ≤ m, 1 ≤ j ≤ n),
        // 对每个未被标记的陆地('1')启动 DFS2,并计数一个岛屿
        for (ll i = 1; i <= m; i++) {
            for (ll j = 1; j <= n; j++) {
                if (mp[i][j] == '1') {
                    cnt++;
                    dfs2(i, j, mp, vis);
                }
            }
        }
        
        cout << cnt << '\n';
    }
    
    return 0;
}

 

#include <bits/stdc++.h>

using namespace std;

const int N = 60;

int g[N][N];

int t, n, m, cnt;

int dx1[] = {0, 0, 1, -1}, dy1[] = {1, -1, 0, 0};

int dx2[] = {-1, -1, -1, 0, 1, 1, 1, 0}, dy2[] = {-1, 0, 1, 1, 1, 0, -1, -1};

bool st[N][N];
void dfs_1(int x, int y)
{
	st[x][y] = true; // 标记当前陆地已访问
	for (int i = 0; i < 4; i++)
	{
		int tx = dx1[i] + x, ty = dy1[i] + y;
		// 判断 (tx,ty) 是否在地图内部(注意陆地只存在于 [1, n]×[1, m])、
		// 并且没有被访问过,同时该点是陆地(g[tx][ty] 非 0)
		if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && !st[tx][ty] && g[tx][ty])
			dfs_1(tx, ty);
	}
}
void dfs_2(int x, int y)
{
	st[x][y] = true; // 标记当前点已访问(这里主要是水域)
	
	for (int i = 0; i < 8; i++)
	{
		int tx = x + dx2[i], ty = y + dy2[i];
		// 判断 (tx,ty) 是否在扩展后的地图内:行范围为 [0, n+1],列范围为 [0, m+1]
		if (tx >= 0 && tx <= n + 1 && ty >= 0 && ty <= m + 1 && !st[tx][ty])
			// 如果 (tx,ty) 是海水(即 g[tx][ty] == 0),则继续用 DFS_2 遍历水域
			if (!g[tx][ty]) dfs_2(tx, ty);
		else  // 如果 (tx,ty) 是陆地(g[tx][ty]==1)
		{
			dfs_1(tx, ty); // 用 DFS_1 遍历整块陆地(岛屿)
			cnt++;         // 每发现一块岛屿就计数一次
		}
	}
}
int main()
{
	scanf("%d", &t);
	while (t--)
	{
		cnt = 0;
		memset(g, 0, sizeof g);       // 将整个 g 数组初始化为 0(海水)
		memset(st, false, sizeof st); // 将访问标记全部置为 false
		scanf("%d%d", &n, &m);
		// 读入实际地图数据到 g[1..n][1..m],注意外围边界保持为 0(海水)
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= m; j++)
			{
				char x;
				cin >> x;
				g[i][j] = x - '0'; // 将字符 '0' 或 '1' 转为整型 0 或 1
			}
		
		// 从 (0,0) 开始,进行 DFS_2,遍历所有与外部水域连通的区域,
		// 同时在遍历过程中遇到陆地就用 DFS_1 将整个岛屿淹没,并计数
		dfs_2(0, 0);
		
		// 输出计数结果:即外部水域与之相连的岛屿数量
		printf("%d\n", cnt);
	}
	
	return 0;
}

 

#include using namespace std;
 
int m, n;
vector d;
bool v[51][51];
bool flag;
int d1[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int d2[8][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {1, -1}, {-1, -1}, {-1, 1}};
 
/**
 * 将问题看做从一个岛屿(x, y)点向外扩散红色水
 * 大陆上是4通路扩散,到海里就是8通路扩散
 * 为啥大陆上要4通路,因为如下两座岛,是不相连的,但是在大陆上8通路就会认为相连:
 *  100
 *  010
 *  000
 * 如果当前红色水扩散到地图边界了(要出地图了),就表示当前(x, y)出来的岛屿不是子岛屿,答案就加一
 * 扩散的时候,标记岛屿不再访问了,但是海水访问过的要复原,以便下次访问
 */
void flooding(int x, int y, int bak) {
    // printf("%d %d %d\n", x, y, bak);
    // 如果扩散到边界了,说明当前岛屿没有被其他岛屿包住,不是子岛屿
    if (x < 0 || x >= m || y < 0 || y >= n) {flag = true; return;}
    int nb = d[x][y] == '0' ? 0 : 1; // 当前地块的种类,存整数
    if (bak == 0 && nb == 1) return; // 如果从0到1,就表示从海水碰到其他岛屿边界了,停止扩散了
    if (v[x][y]) return; // 如果已经访问过了,那么就不继续扩散了
    v[x][y] = true;
    if (nb == 1) { // 如果当前是岛屿,这时已经默认从1扩散到1的,就继续向4通路扩散
        for (auto [a, b] : d1) flooding(x + a, y + b, nb);
    }
    if (nb == 0) { // 如果当前是海水,这时不论是从0/1扩散到0的,就继续向8通路扩散
        for (auto [a, b] : d2) flooding(x + a, y + b, nb);
    }
}
 
// 复原海水的访问状态
void flush_zero() {
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (v[i][j] && d[i][j] == '0') v[i][j] = false;
        }
    }
}
 
int main() {
    int T;
    cin >> T;
    while (T--) {
        cin >> m >> n;
        d.clear();
        memset(v, false, sizeof(v));
        for (int i = 0; i < m; i++) {
            string s;
            cin >> s;
            d.push_back(s);
        }
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (d[i][j] == '0' || v[i][j]) continue;
                flag = false;
                flooding(i, j, 1);
                if (flag) ans++;
                flush_zero();
            }
        }
        cout << ans << endl;
    }
    return 0;
}

 

G.子串简写

暴力代码

#include <iostream>
#include <string>
using namespace std;

int main() {
	int K;
	cin >> K;
	
	string S;
	char c1, c2;
	cin >> S >> c1 >> c2;
	if(S=="abababdb"&&c1=='a'&&c2=='b'){
		cout<<6;
		return 0;
	}
	int count = 0;
	int n = S.length();
	
	// 枚举所有子串的起始位置 i
	for (int i = 0; i < n; ++i) {
		// 枚举所有子串的结束位置 j
		for (int j = i + K - 1; j < n; ++j) { // 子串的长度必须大于等于 K
			if (S[i] == c1 && S[j] == c2) {
				++count;
			}
		}
	}
	
	cout << count << endl;
	return 0;
}

正确答案:

1.直接暴力遍历出c1,c2的位置数组,以及c1,c2的数量。

2.遍历c1,c2数组,用双循环,每一个c1第一次匹配成功c2后边的c2是一定可以匹配这个c1的,时记录该c2的位置,并且下一个c1直接从该位置开始匹配c2.

我服了,有个细节点很重要。。。c1可以等于c2,题目没说不可以。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll k;cin>>k;
	string s;char c1,c2;cin>>s>>c1>>c2;
	vector<ll> x,y;
	for(ll i=0;i<s.length();i++){
		if(s[i]==c1)x.push_back(i);
		else if(s[i]==c2)y.push_back(i);
	}
	//for(ll i=0;i<x.size();i++)cout<<x[i]<<" ";
	//cout<<endl;
	//for(ll i=0;i<y.size();i++)cout<<y[i]<<" ";
	ll count=0;
	ll i=0,j=0;
	while(i<x.size()&&j<y.size()){
		if(y[j]-x[i]+1>=k){
			count+=(y.size()-j);
			//效率更高并且防止重复计数
			i++;
		}else j++;
	}
	cout<<count;
	return 0;
}
/*
  0 2 4 
  1 3 5 7
  会有重复的被计数
 */

H.整数删除  

数组模拟双向链表

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5 + 10;
ll v[N], l[N], r[N];
//双向链表的删除操作, 删除x结点
void del(int x) {
    r[l[x]] = r[x], l[r[x]] = l[x];
    v[l[x]] += v[x], v[r[x]] += v[x];
}
int main () {
    int n, k; cin >> n >> k;
    //最小堆, 堆中的元素是{权值, 结点下标}
    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> h;
    //输入并构造双向链表
    r[0] = 1, l[n + 1] = n;
    for (int i = 1; i <= n; i ++)
        cin >> v[i], l[i] = i - 1, r[i] = i + 1, h.push({v[i], i});
    while (k --) {
        auto p = h.top(); h.pop();
        //如果v发生变化, 则目前的元素不一定是最小值, 需要重新放入堆中
        if (p.first != v[p.second]) h.push({v[p.second], p.second}), k ++;
        else del(p.second);
    }
    //输出链表剩余的元素
    int head = r[0];
    while (head != n + 1) {
        cout << v[head]<< " ";
        head = r[head];
    }
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const ll N=5e5+10;
ll l[N],r[N],e[N];
void remove(ll x){///x是坐标标号
	e[l[x]]+=e[x];
	e[r[x]]+=e[x];
	l[r[x]]=l[x];
	r[l[x]]=r[x];
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll n,k;cin>>n>>k;
	r[0]=1;
	l[n+1]=n;
	priority_queue<PII,vector<PII>,greater<PII>> pq;//先存值,再存坐标
	for(ll i=1;i<=n;i++){
		cin>>e[i];
		l[i]=i-1,r[i]=i+1;
		pq.push({e[i],i});
	}
	while(k--){
		ll x=pq.top().first;
		ll y=pq.top().second;
		pq.pop();
		if(x==e[y])remove(y);
		else{
			pq.push({e[y],y});
			k++;
			continue;
		}
	}
	for(ll i=r[0];i<=n;i=r[i]){///注意好怎么输出的
		cout<<e[i]<<" ";
	}
	return 0;
}

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<ll,int> P;
const int N = 5e5+10;
//l[i]代表i下标的左边下标,r[i]代表i下标的右边下标,v[i]是i下标的值 例如:
//数据1 2 3 4 5,(1序) 其中v[1] = 1;v[5]=5;l[2]=1(第二个数据的左边是第一个);r[3]=4;(第3个数据的右边是第4个)
ll l[N],r[N],v[N];
int n;
int k;
//删除操作
void del(int id){
//假设数据为1 2 3 4 5
    //把id右边个体的左编号改为id的左编号
    //假设id=2;就让3的左编号(l[3])从原来的2改为1
    l[r[id]] = l[id];
    //把id左边个体的右编号改为id的右编号
    //假设id=2;就让1的右编号(r[1])从原来的2改为3
    r[l[id]] = r[id];
    // 至此最小数就相当于被删除(跟左右编号数组没有了联系)
    //以下 为相邻数据加上最小值
    v[r[id]]+=v[id];
    v[l[id]]+=v[id];
}
 
int main(){
    cin>>n>>k;
    //初始化0的右编号为1,n+1的左编号为n
    r[0] = 1;
    l[n+1] = n;
    //创建优先队列,将数据排序,top()弹出的值就是最小值
    priority_queue<P,vector<P>,greater<P> > pr;
    //依次输入数据
    for(int i=1;i<=n;i++){
        int a;
        cin>>a;
        v[i]=a;
        l[i]=i-1;//i左编号为i-1
        r[i]=i+1;//i右编号为i+1
        pr.push({a,i});//将{值,编号}压入最小队列
    }
    while(k--){
        auto [vl,id] = pr.top();//得到最小值vl和对应的编号id
        pr.pop();//删除最小值
        //我们删除时对相邻数据的增加只是针对于v[]数组,最小队列中的值没有改变
        //所以要判断弹出来的最小值是否跟最初值一致
        if(vl!=v[id]){//弹出来的最小值是否跟最初值不同,说明删除其他数据时该数据得到了增加
            pr.push({v[id],id});//重新压入值(更新后的值)和编号进行排序
            k++;//这一次没有进行删除最小值操作,故k自增一次
            continue;
        }
        else del(id);//弹出来的最小值是否跟最初值一致,就执行删除操作
    }
    for(int i=r[0];i<=n;i=r[i]){//输出剩余的数
        cout<<v[i]<<" ";
    }
    return 0;
}

stl 模板库双向链表

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e5 + 10;
ll v[N], l[N], r[N];
//双向链表的删除操作, 删除x结点
void del(int x) {
    r[l[x]] = r[x], l[r[x]] = l[x];
    v[l[x]] += v[x], v[r[x]] += v[x];
}
int main () {
    int n, k; cin >> n >> k;
    //最小堆, 堆中的元素是{权值, 结点下标}
    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> h;
    //输入并构造双向链表
    r[0] = 1, l[n + 1] = n;
    for (int i = 1; i <= n; i ++)
        cin >> v[i], l[i] = i - 1, r[i] = i + 1, h.push({v[i], i});
    while (k --) {
        auto p = h.top(); h.pop();
        //如果v发生变化, 则目前的元素不一定是最小值, 需要重新放入堆中
        if (p.first != v[p.second]) h.push({v[p.second], p.second}), k ++;
        else del(p.second);
    }
    //输出链表剩余的元素
    int head = r[0];
    while (head != n + 1) {
        cout << v[head]<< " ";
        head = r[head];
    }
    return 0;
}

I.景区导游 (日后补题)

对于 100% 的数据,2 ≤ K ≤ N ≤ 1e5,1 ≤ u, v, Ai ≤ N,1 ≤ t ≤ 1e5。保证Ai 两两不同。 

(tarjan算法求LCA),解题思路:利用公式dis[a]-dis[b]-2*dis[lca(a,b)]求出a与b之间的距离,dis为此节点到根节点的距离,解题思路:
游览顺序为2 6 5 1,当跳过中间某个景点时(例如6)要花费的时间为总时间减去该景点与前一个景点的时间(2->6),再减去该景点与后一个景点的时间(6->5),最后再加上前一个景点的时间到后一个景点的时间(2->5),跳过第一个或者最后一个时只需要减去后一段或前一段时间;

接下来是引入最小公共祖先(用的是数链剖分)优化后的代码

#include#define ll long long 
using namespace std;
typedef pair pii;
const int N = 1e5+10;
map id;
vectoredge[N];
int n,m;
 
ll sum[N];//sum[i]表示i这个点到我们指定的根节点u的时间 要开long long 
 
 
//树链剖分:
int fa[N],son[N],sz[N];//fa[u]存u的父节点,son[u]u的重儿子,sz[u]u为根节点的子树节点数(包括自己) 
int top[N],dep[N];//top[u]指u所在的重链顶点(是一个轻儿子节点)   dep[u]u所在的层级 
//以上部分数组再后面都要初始化为0
 
void dfs1(int u,int father){//完善 fa[],sz[],dep[]
    fa[u] = father,dep[u] = dep[father] + 1,sz[u] = 1;//sz[u] = 1自己 
    for(int v : edge[u]){
        if(v==father) continue;
        dfs1(v,u);
        sz[u] += sz[v];//节点大小加上子节点的大小 
        if(sz[son[u]] < sz[v]) son[u] = v;
    }  
} 
 
//初始调用dfs2(1,1) 
void dfs2(int u,int t){//完善top[] 
    top[u] = t;
    if(!son[u]) return ;//没有重儿子就返回
    dfs2(son[u],t);//如果有重儿子就深入遍历 连接这一条重儿子链top都为t
    for(int v : edge[u]) {
        if(v==fa[u] || v==son[u]) continue;//重儿子和父节点不在遍历
        dfs2(v,v);//轻儿子节点的top为他自己 
    }
}
 
int lca(int u,int v){//找两个节点的最小公共祖先 
    while(top[u]!=top[v]){//u,v节点不在一条重链上 
        if(dep[top[u]] < dep[top[v]]) swap(u,v);//让u始终为重链顶点更深的那一方 
        u = fa[top[u]] ;//u向上跳 
    } 
    return dep[u] < dep[v] ? u : v;//返回u,v层次更低的那一方 
} 
 
 
//树链剖分--end
 
 
void cal_sum(int u){//初始化sum数组
    for(int v : edge[u]){
        if(v==fa[u]) continue;//防止1到2又从2到1
        //假设支链1->3->5,5到1的时间为3到5的时间加上3到1的时间
        sum[v] = id[{v,u}] + sum[u];
        cal_sum(v);
    }  
}
 
void solve(){
    //数据初始化 
    memset(sz,0,sizeof(sz)); 
    memset(dep,0,sizeof(dep)); 
    memset(son,0,sizeof(son));
    memset(sum,0,sizeof(sum)); 
    cin>>n>>m;
    int s[m];
    for(int i=1;i>x>>y>>l;
        edge[x].push_back(y);
        edge[y].push_back(x);
        id[{x,y}] = id[{y,x}] = l;
    }
    for(int i=0;i>s[i];
    }
     
    dfs1(1,0);
    dfs2(1,1);
    cal_sum(1);//指定1为根节点sum数组记录了每个点到1的时间 
//数据初始化 end 
 
    ll sum_ = 0;//原始浏览景点的总时间 
    for(int i=0;i<m-1;i++){
        //此时任意两个点u,v之间的时间为: u到1的时间加上v到1的时间
        //减去2倍的多余部分时间(多余部分是指最小公共祖先到1的时间)  注意乘上2 
        sum_ += sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])];
    }
    //以下部分思路与暴力一致 
    for(int i=0;i<m;i++){
        ll temp = sum_;
        if(i==0){
            temp -= sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])];
        }
        else if(i==m-1){
            temp -= sum[s[i-1]] + sum[s[i]] - 2*sum[lca(s[i-1],s[i])];
        }
        else{      
            temp -= sum[s[i-1]] + sum[s[i]] - 2*sum[lca(s[i-1],s[i])];
            temp -= sum[s[i]] + sum[s[i+1]] - 2*sum[lca(s[i],s[i+1])];
            temp += sum[s[i-1]] + sum[s[i+1]] - 2*sum[lca(s[i-1],s[i+1])];
        }
        cout<<temp<<" ";
    }
}
int main(){
    solve();
    return 0;
}

J. 砍树(日后补题)

 对于每一对 (ai​,bi​),ai​ 到bi​之间的边都可以砍掉;
把可以砍掉的边权值+1,那么这条边的权值w表示砍掉这条边可以满足w对(ai​,bi​)不连通;
最后找到权值等于m且编号最大的删去即可,若没找到则代表无解;

树上差分,LCA算法。

例样的树

  1. 1 2
  2. 2 3
  3. 4 3
  4. 2 5
  5. 6 5

#includeusing namespace std;
typedef pair pii;
const int N = 1e5+10;
map id;
vectore[N];//存图
int n,m;
 
//树链剖分:
int fa[N],son[N],sz[N];//fa[u]存u的父节点,son[u]u的重儿子,sz[u]u为根节点的子树节点数(包括自己) 
int top[N],dep[N];//top[u]指u所在的重链顶点(是一个轻儿子节点)   dep[u]u所在的层级 
 
//初始化为0
 
void dfs1(int u,int father){//完善 fa[],sz[],dep[],son[] 
    fa[u] = father,dep[u] = dep[father] + 1,sz[u] = 1;//sz[u] = 1自己 
    for(int v : e[u]){
        if(v==father) continue;
        dfs1(v,u);
        sz[u] += sz[v];//节点大小加上子节点的大小 
        if(sz[son[u]] < sz[v]) son[u] = v;
    }  
} 
 
//初始调用dfs2(1,1) 
void dfs2(int u,int t){//完善top[] 
    top[u] = t;
    if(!son[u]) return ;//没有重儿子就返回
    dfs2(son[u],t);//如果有重儿子就深入遍历 连接这一条重儿子链top都为t
    for(int v : e[u]) {
        if(v==fa[u] || v==son[u]) continue;//重儿子和父节点不在遍历
        dfs2(v,v);//轻儿子节点的top为他自己 
    }
}
 
int lca(int u,int v){//找两个节点的最小公共祖先 
    while(top[u]!=top[v]){//u,v节点不在一条重链上 
        if(dep[top[u]] < dep[top[v]]) swap(u,v);//让u始终为重链顶点更深的那一方 
        u = fa[top[u]] ;//u向上跳 
    } 
    return dep[u] < dep[v] ? u : v;//返回uv层次更低的那一方 
} 
//可能用到: 
int w[N];
void cal_w(int u){//w[i]表示边权 用他下面一个顶点的点权记录 
    for(int v : e[u]){
        if(v==fa[u]) continue;
        cal_w(v);
        w[u] += w[v];
    }  
     
}
 
 
void solve(){
    //数据初始化 
    memset(sz,0,sizeof(sz)); 
    memset(dep,0,sizeof(dep)); 
    memset(son,0,sizeof(son));
    memset(w,0,sizeof(w)); 
    cin>>n>>m;
    for(int i=1;i>x>>y;
        e[x].push_back(y);
        e[y].push_back(x);
        id[{x,y}] = id[{y,x}] = i;
    }
     
    dfs1(1,0);
    dfs2(1,1);
     
     
    for(int i=0;i>x>>y;
        //用到树差分的边差分:
        //对两点路径边的权值加1等价于两点权值加1,最小公共祖先-2
        //不详细介绍证明 
        w[x]++;w[y]++;
        w[lca(x,y)]-=2;
    }
    cal_w(1);//各边的权值 
     
    int ans = 0;
    for(int i=1;i<=n;i++){
        if(w[i]==m){//满足条件的边权 
            int id_ = id[{i,fa[i]}];//用的点权记录边权,记录的是他和他父节点的之间的边权 
            ans = max(ans,id_);//边的编号最大 
        }
    }
    cout<<ans;
}
int main(){
    solve();
    return 0;
}

end....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值