Contest3400 - 2024寒假集训 进阶训练赛 (十三)

目录

问题 A: 符文宗师的魔方阵

问题 B: APP的成绩单

问题 C: 6.1.4.1 最大的节点

问题 D: 6.2.2.1 油田

问题 E: Digit sum

问题 G: 5.5 一山不容二虎

问题 H: 2.4.8.1 集合合并

问题 J: 2.4.9.1 硬木种类

问题 K: 2.4.3 骑士移动

问题 L: 衔尾之蚯蚓


问题 A: 符文宗师的魔方阵

题目描述

符文宗师是艾里奥斯大陆的一名魔法骑士,通过将不同符文力量结合到剑术中来增强自己的力量。
为了寻求更多强大的符文力量,他开始游历大陆。
一天,他在山涧中发现了一个刻有神秘数字阵列的发光石。多年的冒险经验告诉他,这个发光石不简单,其内隐藏着强大的符文力量。
为了解开发光石的秘密,他去拜访了他的一个炼金术士好友——艾伦。
艾伦告诉他,这个发光石上面的神秘数字阵列是数学中的魔方阵,所谓魔方阵,指的是一个矩阵,它的各行各列的和相同。但是这个魔方阵有一部分数字缺失了,如果能补全这一残缺的魔方阵,那么其内的符文力量自会显现。
艾伦将发光石上面残缺的魔方阵以简单的形式抄了下来,如样例输入所示,方便进行填充。此外,艾伦还通过占星术发现,这个残缺的魔方阵,不会出现一个子矩阵上面的数字完全残缺的情形。

符文宗师向你寻求帮助,希望你能够帮他填充这个残缺的魔方阵。如果你能帮他补全残缺的魔方阵,那么你将能够习得新技能——月光之刃

输入

输入是一个5行、5列的残缺的魔方矩阵,矩阵的每个元素的取值大于等于1,小于等于100。
魔方阵中缺失数字的个数大于等于1,小于等于4。
为了方便,缺失数字的位置填上了-1。
并且这个残缺的魔方阵中不会出现如下的子矩阵: 
 -1  -1 
 -1  -1 
具体的输入,可以参考下面的输入样例。

输出

填补完整后的魔方阵。
 

样例输入 复制
 17  24   1   8  15
 23   5   7  -1  16
  4   6  13  20  22
 10  -1  19  21   3
 11  18  25   2   9
样例输出 复制
 17  24   1   8  15
 23   5   7  14  16
  4   6  13  20  22
 10  12  19  21   3
 11  18  25   2   9
提示

每个数字输出时的格式控制符为"%3d".

AC代码:
//abgikl
#include <bits/stdc++.h>
using namespace std;

int main() {
	int a[10][10], number[10];
	for (int i = 0; i < 5; i++) {
		int sum = 0;
		for (int j = 0; j < 5; j++) {
			cin >> a[i][j];
			if (a[i][j] == -1)
				sum++;
		}
		number[i] = sum;
	}
	for (int i = 0; i < 5; i++) {
		int sum = 0;
		for (int j = 0; j < 5; j++) {
			if (a[j][i] == -1)
				sum++;
		}
		number[i + 5] = sum;
	}
	int sum[10];
	memset(sum, 0, sizeof(sum));
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (a[i][j] != -1)
				sum[i] += a[i][j];
			if (a[j][i] != -1)
				sum[i + 5] += a[j][i];
		}
	}
	int ans = 0;
	for (int i = 0; i < 5; i++) {
		if (number[i] == 0) {
			ans = sum[i];
			break;
		}
	}
	if (ans == 0) {
		for (int i = 0; i < 5; i++) {
			if (number[i + 5] == 0) {
				ans = sum[i + 5];
				break;
			}
		}
	}
	for (int i = 0; i < 5; i++) {
		if (number[i] == 1) {
			for (int j = 0; j < 5; j++) {
				if (a[i][j] == -1) {
					a[i][j] = ans - sum[i];
					break;
				}
			}
		}
	}
	for (int i = 0; i < 5; i++) {
		if (number[i + 5] == 1) {
			for (int j = 0; j < 5; j++) {
				if (a[j][i] == -1) {
					a[j][i] = ans - sum[i + 5];
					break;
				}
			}
		}
	}
	for (int i = 0; i < 5; i++) {
		int s = 0;
		for (int j = 0; j < 5; j++) {
			if (a[j][i] != -1)
				s += a[j][i];
		}
		sum[5 + i] = s;
	}

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (a[j][i] == -1) {
				a[j][i] = ans - sum[i + 5];
			}
		}
	}

	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			printf("%3d ", a[i][j]);
		}
		printf("\n");
	}

	return 0;
}

问题 B: APP的成绩单

题目描述

APP同学不小心弄乱了班级成绩Excel表格,还是不能撤销的那种。

比如原表格的内容为(按次序分别为:姓名、学号、性别、成绩;用空格分隔):
appmlk 2020401190 girl 80.8
notappmlk 2020401191 boy 98.0
isappmlk 2020401192 boy 100.0


但是弄乱后变为:
2020401190 appmlk 80.8 girl
boy 2020401191 98.0 notappmlk
isappmlk 100.0 2020401192 boy
不过好在只是把每一行内部的顺序弄乱了,行与行之间的数据没有弄混,请你写一个程序帮他把表格内容恢复原状。

输入

第一行包含一个正整数n(1<= n <= 100),代表学生人数。
接下来的n行,每行为每个学生的信息,中间用单个空格隔开;其中必定包含单个学生的姓名、学号、性别和成绩。

姓名只可能包含英文字符、且长度必定超过一个英文字符,且少于50个字符(且不可能与性别的单词重复)。
学号只可能包含数字、且长度必定为10个数字。
性别只可能为“boy”或者“girl”。
成绩只可能为数值,且必定包含一个小数点,小数点后仅保留一位。

输出

把每一行的学生信息按照姓名、学号、性别、成绩排列
学生的排名顺序不变

样例输入 复制
3
2020401190 appmlk 80.8 girl
boy 2020401191 98.0 notappmlk
isappmlk 100.0 2020401192 boy
样例输出 复制
appmlk 2020401190 girl 80.8
notappmlk 2020401191 boy 98.0
isappmlk 2020401192 boy 100.0
AC代码: 
//bgikl
#include <bits/stdc++.h>
using namespace std;

int main() {
	int n;
	string s, name, num, score, gender;
	cin >> n;
	while (n--) {
		for (int i = 0; i < 4; i++) {
			cin >> s;
			if (s == "girl" || s == "boy")
				gender = s;
			else if ( s[0] >= 'a' && s[0] <= 'z')
				name = s;
			else if (s.size() == 10 && s[0] >= '0' && s[0] <= '9')
				num = s;
			else
				score = s;
		}
		cout << name << " " << num << " " << gender << " " << score << endl;
	}
	return 0;
}

问题 C: 6.1.4.1 最大的节点

题目描述

给 N 出个点,M 条边的有向图,对于每个点 v,求 A(v) 表示从点出发,能到达的编号最大的点。

输入

第1 行,2 个整数 N, M。 

接下来 M 行,每行 2 个整数Ui,Vi,表示边(Ui,Vi)。点用 1, 2, ⋯, N 编号。

输出

N个整数 A(1), A(2), ⋯, A(N)。

样例输入 复制
4 3
1 2
2 4
4 3
样例输出 复制
4 4 3 4
提示

1 ≤ N, M ≤ 100000。

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

const int MAXN = 1000; // 根据问题描述调整最大节点数

int reachable[MAXN][MAXN]; // 可达矩阵

int main() {
	int N, M, u, v;
	cin >> N >> M;

	// 初始化可达矩阵
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			reachable[i][j] = (i == j) ? 1 : 0; // 初始化对角线为1,其余为0
		}
	}

	// 读入边并更新可达矩阵
	for (int i = 0; i < M; i++) {
		cin >> u >> v;
		reachable[u - 1][v - 1] = 1; // 节点编号从0开始
	}

	// 使用Floyd Warshall算法计算传递闭包
	for (int k = 0; k < N; k++) {
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (reachable[i][k] && reachable[k][j]) {
					reachable[i][j] = 1;
				}
			}
		}
	}

	// 输出每个节点可达的节点数
	for (int i = 0; i < N; i++) {
		for (int j = N - 1; j >= 0; j--) {
			if (reachable[i][j] == 1) {
				cout << j + 1 << " ";
				break;
			}
		}
	}


	return 0;
}

问题 D: 6.2.2.1 油田

题目描述

石油勘探公司正在按计划勘探地下油田资源。油田是一片长方形地域,工人们将该地域划分为许多小正方形区域,然后使用探测设备分别探测在每一小块正方形区域内是否有油。
含有油的区域被称为油田,如果两个油田相邻(8联通:包括水平、垂直、对角线相邻),则它们是相同油藏的一部分。油藏可能非常大并可能包含许多油田(油田的个数不超过100)。你的工作是确定在这个长方形地域中包含多少不同的油藏。

输入

输入一个或多个长方形地域。
每个地域的第1行为两个正整数m和n(1≤m,n≤100),分别表示地域的行数和列数;
如果m=0或n=0 表示输入结束,否则此后有m行,每行都有n个字符。
每个字符都对应一个正方形区域,字符*表示没有油,字符@表示有油。

输出

单行输出每个地域的油藏个数。

样例输入 复制
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5
****@
*@@*@
*@**@
@@@*@
@@**@
0 0
样例输出 复制
0
1
2
2

 ac代码:

#include<stdio.h> 
#include<string.h>
int vis[110][110], m, n, cnt;
char mp[110][110];
int next[8][2] = {-1,0, -1,1, 0,1, 1,1, 1,0, 1,-1, 0,-1, -1,-1};
void dfs(int x, int y) {
//	vis[x][y] = 1;
	int tx, ty;
	for (int k = 0; k < 8; k++) {// 利用循环朝八个方向遍历 
		//更新变量 
		tx = x + next[k][0];
		ty = y + next[k][1];
		
		//判断边界 
		if (tx < 1 || tx > m || ty < 0 || ty >= n) continue;
		
//		if (mp[x][y] == '*' || vis[x][y] != 0) continue;
		
		// 这个点必须,没走过 ,并且是油田 ‘@ ’ 
		if (!vis[tx][ty] && mp[tx][ty] == '@') {
			vis[tx][ty] = 1;// 标记已经走过了
			//向下一个点深搜 
			dfs(tx, ty);
		}
	}
	return ;
	
}
 
int main() {
	while (scanf("%d%d", &m, &n) && m && n) {
//		getchar();
		for(int i = 1; i <= m; i++) {
			scanf("%s", mp[i]);// 说明后面要从 第 0 列开始遍历 
		}
		//初始化记号数组 vis 
		memset(vis, 0, sizeof(vis));
		cnt = 0;
		
		for (int i = 1; i <= m; i++) {
			for (int j = 0; j < n; j++) {//错误原因: 1,j应该从零开始,因为输入时一次输入一行 默认从第 0  列开始 
				if (!vis[i][j] && mp[i][j] == '@') {
//					vis[i][j] = 1;
					dfs(i, j);
					cnt++;
				}
			}
		}
		printf("%d\n", cnt);
	}
}

问题 E: Digit sum

#include <iostream>
//#include <cstring>
#include <cstdio>
using namespace std;
int a[1000005][10];
int main()
{
	int T,n,b,tag=0,tmp;
	//memset(dp,0,sizeof(dp));
	for(int j=2; j<=10; j++){
		for(int i=1; i<=1000000; i++){
			tmp = i;
			while(tmp){
				a[i][j] += tmp%j;
				tmp/=j;
			}
			a[i][j] += a[i-1][j];
		}
	}
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&b);
		printf("Case #%d: %d\n",++tag,a[n][b]);
	}
	return 0;
}

问题 G: 5.5 一山不容二虎

题目描述

在n×n的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之在同一行、同一列、同一斜线上的棋子。设计算法在n×n的棋盘上放置n个皇后,使其彼此不受攻击。

输入

样例组数
t ( 0 < t < 20 )
皇后的个数
n ( 0 < n < 20 )

输出

可以摆放的方案总数
ans

样例输入 复制
1
4
样例输出 复制
2

AC代码: 

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define int long long
#define sc(x) scanf("%lld",&x)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (-x&x)
struct myhash {static uint64_t fxn(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM =chrono::steady_clock::now().time_since_epoch().count();return fxn(x + FIXED_RANDOM);}};
int qmi(int a,int b){int res = 1;while(b){if(b&1){res = res*a;}b = b>>1;a = a*a; } return res;}
int n;
int a[100][100];
int ans = 0;
bool c[100],dui1[100],dui2[100];
void dfs(int x){
    if(x>n){
        ans++;
        return;
    }
    for(int j=1;j<=n;++j){
        int x1 = j-x+n;
        int x2 = x+j;
        if(c[j]==false&&dui1[x1]==false&&dui2[x2]==false){
            c[j] = true;
            dui1[x1] = true;
            dui2[x2] = true;
            dfs(x+1);
            c[j] = false;
            dui1[x1] = false;
            dui2[x2] = false;
        }
    }
}
void solve()
{
    memset(a,0,sizeof a);
    ans = 0;
    cin>>n;
    dfs(1);
    cout<<ans<<endl;
}
signed main()
{
    //freopen("D:/vsCode/.vscode/oi/in.txt","r",stdin);
    //freopen("D:/vsCode/.vscode/oi/out.txt","w",stdout);
    IOS
    int t = 1;
    cin>>t;
    while(t--) solve();
    return 0;
}

摘抄自:http://t.csdnimg.cn/jmc5L 

问题 H: 2.4.8.1 集合合并

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

int main() {
	int n, m;
	while (cin >> n >> m) {
		set<int> s;
		for (int i = 0; i < n + m; i++) {
			int a;
			cin >> a;
			s.insert(a);
		}
		for (auto it = s.begin(); it != s.end(); it++) {
			cout << *it << " ";
		}
		cout << endl;
	}
	return 0;
}

问题 J: 2.4.9.1 硬木种类

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

int main() {
	map<string, double> mp;
	string s;
	int cnt = 0;
	while (getline(cin, s)) {
		mp[s]++;
		cnt++;
	}
	for (auto it = mp.begin(); it != mp.end(); ++it) {
		it->second = 1.0 * 100 * it->second / cnt;
		cout << it->first << " " << fixed << setprecision(4) << it->second << endl;
	}
	return 0;
}

问题 K: 2.4.3 骑士移动

题目描述

写程序,计算象棋中马从一个位置移动到另一个位置所需的最少移动次数。

输入

有多组测试数据。

第一行一个整数 T,代表数据组数。
每组数据包含三行。
第一行表示棋盘的长度 L,棋盘大小为 L×L。
第二行包含两个整数 x, y,表示马的起始位置坐标。
第三行包含两个整数 a, b,表示马的终点位置坐标。
L 最大为 300。
棋盘坐标范围为 [0, ..., L-1]。

输出

对于每组数据输出一行,包含一个数字,即最少移动次数。
若起点终点相同,则移动次数为 0。

样例输入 复制
3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1
样例输出 复制
5
28
0
提示

马走日!!!
数据保证可以到终点,不用额外判断无法走到终点的情况。

AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define int long long
#define sc(x) scanf("%lld",&x)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (-x&x)
struct myhash {static uint64_t fxn(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM =chrono::steady_clock::now().time_since_epoch().count();return fxn(x + FIXED_RANDOM);}};
int qmi(int a,int b){int res = 1;while(b){if(b&1){res = res*a;}b = b>>1;a = a*a; } return res;}
int sx,sy,ex,ey;
int n;
int dx[8] = {-2,-1,1,2,2,1,-1,-2};
int dy[8] = {1,2,2,1,-1,-2,-2,-1};
bool st[301][301];
int dis[301][301];
int bfs()
{
    queue<pair<int,int>> q;
    q.push({sx,sy});
    dis[sx][sy] = 0;
    while(q.size()){
        auto t = q.front();
        q.pop();
        int x = t.first;
        int y = t.second;
        st[x][y] = true;
        for(int i=0;i<8;++i){
            x += dx[i]; y+=dy[i];
            if(x>=0&&x<=n-1&&y>=0&&y<=n-1&&st[x][y]==false){
                q.push({x,y});
                st[x][y] = true;
                dis[x][y] = dis[x-dx[i]][y-dy[i]]+1;
                if(x==ex&&y==ey){
                    return dis[x][y];
                }
            }
            x-=dx[i];
            y-=dy[i];
        }
    }
 
}
void solve()
{
    memset(st,0,sizeof st);
    memset(dis,0x3f,sizeof dis);
    cin>>n>>sx>>sy>>ex>>ey;
    if(sx==ex&&sy==ey){
        cout<<"0"<<endl;
        return;
    }
    cout<<bfs()<<endl;
}
signed main()
{
    //freopen("D:/vsCode/.vscode/oi/in.txt","r",stdin);
    //freopen("D:/vsCode/.vscode/oi/out.txt","w",stdout);
    IOS
    int t = 1;
    cin>>t;
    while(t--) solve();
    return 0;
}

摘抄自:http://t.csdnimg.cn/jmc5L

问题 L: 衔尾之蚯蚓

题目描述

众所周知,蚯蚓剁成两半之后还能再生,那么重生之后的蚯蚓,它的头和尾是怎么定义的呢?小编也不知道。
现在我们从不大于N的正整数中取出两个数A和B
当A和B以10为基数,并且没有前导零时,A的最后一位数字等于B的第一位,A的第一位数字等于B的最后一位数字。这样就算得上一对合格的蚯蚓(?)
给你一个正整数N,求出这种合格的数对的数量。

输入

N
1≤N≤2×105

输出

符合条件的(A,B)的数量

样例输入 复制
25
样例输出 复制
17
提示

样例说明:
(1,1),(1,11),(2,2),(2,22),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(11,1),(11,11),(12,21),(21,12),(22,2),(22,22)

 AC代码:

 

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define int long long
#define sc(x) scanf("%lld",&x)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define lowbit(x) (-x&x)
struct myhash {static uint64_t fxn(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM =chrono::steady_clock::now().time_since_epoch().count();return fxn(x + FIXED_RANDOM);}};
int qmi(int a,int b){int res = 1;while(b){if(b&1){res = res*a;}b = b>>1;a = a*a; } return res;}
void solve()
{
    int n; cin>>n;
    if(n%10==0) n = n-1;//n能整除10的时候,n这个数字有后缀0无效,为了方便后续的if判断进行-1。
    if(n<=9){//特殊处理<=9的情况
        cout<<n<<endl;
        return;
    }
    string p = to_string(n);
    int maxlen = to_string(n).size();//n的长度
    int maxl = p[0] - '0';//n的最高位
    int maxr = p[p.size()-1] - '0';//n的最低位
    int maxmid = n - maxr - maxl*qmi(10,maxlen-1);//n除去两边两个数之后中间的数字。
    int ans = 0;
    int leftlen = maxlen - 2;//减去2之后剩余的数位位数。
    for(int i=1;i<=n;++i){
        if(i%10!=0){
            string s = to_string(i);
            int l = s[0] - '0';
            int r = s[s.size()-1] - '0';//翻转之后r是最高位,l是最低位
            if(l==r) ans++;//所以字符一样,多加1,比如222,它可以和数字2首尾相连
            if(r*10+l<=n) ans++;//中间不加东西的时候,看本身是否超过n
            if(r==maxl){//中间加数字,最高位和n最高位一样
                if(l<=maxr){//最低位小于等于n的最低位
                    ans+=maxmid+1;//根据n的mid中间的数进行加法
                    if(maxmid==0&&r*100+l>n){//maxmid = 0有两个情况一个是没数字,一个是全是0,这里进行没数字的特殊处理
                        ans--;
                    }
                }else{
                    ans+=maxmid;//最低位大于n的最低位,比如28, n = 267,maxmid这时等于6,那么28中间只能插入0~5,6个数字
                }
            }else if(r<maxl){//最高位小于n的最高位
                if(leftlen>0){//剩余位数大于0,比如 n = 267,leftlen = 1,那么就还可以插入一个数字,0~9都可以,也就是10的leftlen次方
                    ans+=qmi(10,leftlen);     
                }
 
            }else{
                if(leftlen-1>0){//最高位大于n的最高位,那么可以插入的数字只能是leftlen - 1
                    ans+=qmi(10,leftlen-1);
                }
            }
        }
    }
    cout<<ans<<endl;
}
signed main()
{
    //freopen("D:/vsCode/.vscode/oi/in.txt","r",stdin);
    //freopen("D:/vsCode/.vscode/oi/out.txt","w",stdout);
    IOS
    int t = 1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

摘抄自:http://t.csdnimg.cn/jmc5L

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solen.&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值