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

问题 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".

代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int a[5][5],m[4]={0};
    int i,j,k,x=0,sum=0,sum1=0;
    for(i=0;i<5;i++)
    {
        for(j=0;j<5;j++)
        {
            scanf("%d",&a[i][j]);
            if(a[i][j]!=-1)
            {
                x++;
                sum1=sum1+a[i][j];
            }
        }
        if(x==5){

            sum=sum1;//当一行之中没有-1时计算总和数sum
            sum1=0;x=0;
        }
    }
    for(i=0;i<5;i++)
    {
        for(j=0;j<5;j++)
        {
            if(a[i][j]!=-1)
                printf("%3d ",a[i][j]);//不是-1时直接输出
            else if(a[i][j]==-1)
            {
                a[i][j]=sum;//发现-1时先将其令为总和数
                for(k=0;k<5;k++)
                {
                    if(k!=j)//减去这一行中除了a[i][j]的每一个数
                        a[i][j]=a[i][j]-a[i][k];
                }
                if(a[i][j]!=-1&&a[i][j+1]!=-1&&i+1!=4) //防止一行有两个-1
                    printf("%3d ",a[i][j]);
                else//如果该行有两个-1,则用列计算
                {
                    a[i][j]=sum;
                    for(k=0;k<5;k++)
                    {
                        if(k!=i)//减去这一列中除了a[i][j]的每一个数
                            a[i][j]=a[i][j]-a[k][j];
                    }
                    cout<<setw(3)<<a[i][j]<<" ";
                }
            }
        }
        cout<<endl;
    }
    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
代码 
#include <bits/stdc++.h>
using namespace std;
int main() {
	int n;
	string s, name, num, as, az;
	cin >> n;
	for(int j=0;j<n;j++) {
		for (int i = 0; i < 4; i++) {
			cin >> s;
			if (s == "girl" || s == "boy")
				az = 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
				as = s;
		}
		cout << name << " " << num << " " << az << " " << as << endl;
	}
}

问题 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 <limits.h>
#include <cstdio>
#include <cmath>
#include <stack>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
#include <queue>
#include <cstring>
#include <fstream>
#include <map>
#include <list>
#include <set>
using namespace std;
int n,m;
vector<int> g[100010];	//反向建图
int res[100010];	//res[i]表示i能达到的最大点,最开始设置为0表示未访问过
void dfs(int now,int from){	//now表示遍历的点,from表示是尽头是from,即可以从now到达from
    res[now]=from;
    for(int k=0;k<g[now].size();k++){
        int to=g[now][k];
        if(!res[to]){
            dfs(to,from);
        }
    }
}
int main(){
    cin>>n>>m;
    while(m--){
        int u,v;
        scanf("%d %d",&u,&v);
        g[v].push_back(u);
    }
    memset(res,0,sizeof(res));
    for(int i=n;i>=1;i--){
        if(!res[i]){
            res[i]=i;
            dfs(i,i);
        }
    }
    for(int i=1;i<=n;i++) printf("%d ",res[i]);
    return 0;
}
来源

【洛谷】P3916 图的遍历_给出 n 个点,m 条边的有向图,对于每个点 v ,求 a ( v ) 表示从点 v 出发,能到达的-CSDN博客

问题 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
代码 
#include<bits/stdc++.h>
#define REP(i,b,e) for(int i=(b);i<=(e);i++)
using namespace std;

const int maxn=100+5;
string str[maxn];//存储字符矩阵 
int m,n,setid[maxn][maxn];//行列,连通分量号 

void dfs(int x,int y,int id)//行列和连通分量号 
{
	if(x<0||x>=m||y<0||y>=n) return ;//出界
	if(setid[x][y]>0||str[x][y]!='@') return ;//已有连通分量号或不是'@' 
	setid[x][y]=id;
	REP(dx,-1,1)
		REP(dy,-1,1)
			if(dx!=0||dy!=0)
				dfs(x+dx,y+dy,id);//八个方向深搜 ,把连通的都赋值一样的数值,就是连通分量号,总之不为0
 }
 
 int main()
 {
 	while((cin>>m>>n)&&m&&n)
 	{
 		REP(i,0,m-1)
 			cin>>str[i];
 		memset(setid,0,sizeof(setid));
 		int cnt=0;
 		REP(i,0,m-1)
 			REP(j,0,n-1)
 				if(setid[i][j]==0&&str[i][j]=='@')//只要为0就表示没有搜过,或者和别的块不连通,就可以是一个新的油藏
 					dfs(i,j,++cnt);
 		cout<<cnt<<endl;
	 }
	 return 0;
  } 
来源

油田(UVA572图的搜索)_python石油勘探公司正在按计划勘探地下油田资源。油田是一片长方形地域,工人们将-CSDN博客

问题 E: Digit sum

 

题目描述

函数Sb(n)是b进制的n所有数位之和。

如S10(233)=2+3+3=8,S2(8)=1+0+0=1,S2(7)=1+1+1=3。
给定N和b,你需要计算 ∑n=1N Sb(n)

输入

输入的第一行给出了测试案例的数量,T。

接下来是T个测试用例。每个测试用例以包含一行.两个整数N和b。

输出

对于每个测试案例,输出一行,格式如 Case #x: y,其中x是测试案例编号(从1开始),y是答案。

样例输入 复制
2
10 10
8 2
样例输出 复制
Case #1: 46
Case #2: 13
代码
#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;
}

 问题 F: 7.2 工厂最大效益

题目描述

某食品加工厂一共有三个车间,第一车间用1个单位的原料 N 可以加工5个单位的产品 A 或 2 个单位的产品 B。产品 A 如果直接售出,售价为10元,如果在第二车间继续加工,则需要额外加工费5元,加工后售价为19元。产品 B 如果直接售出,售价16元,如果在第三车间继续加工,则需要额外加工费4元,加工后售价为24元。原材料 N 的单位购入价为5元,每工时的工资是15元,第一车间加工一个单位的N,需要0.05个工时,第二车间加工一个单位需要0.1工时,第三车间加工一个单位需要0.08工时。每个月最多能得到12000单位的原材料N,工时最多为1000 工时。如何安排生产,才能使工厂的效益最大呢?

输入

非基本变量个数和非基本变量下标
n ( 0 < n < 10 )
a1 a2 ... an
基本变量个数和基本变量下标
m ( 0 < m < 10 )
a1 a2 ... am
约束标准型初始单纯形表参数 ( 0 < aij < 20000 )
a01 a02 ... a0n
a11 a12 ... a0n
...
am1 am2 ... amn


 

输出

最优解
ans

样例输入 复制
3
2 4 5
4
1 3 6 7
0 2.5 2.8 76.25
0 1 0 -5
0 0 1 -2
12000 0 0 1
1000 0.1 0.08 0.05
样例输出 复制
929000
代码
#include<iostream>
#include<math.h>
#include<iomanip>
#include<stdio.h>
using namespace std;
float kernel[100][100];
char FJL[100] = {};
char JL[100] = {};
int n, m;
void print()
{
	cout << endl;
	cout << "---------单纯形表如下:----------" << endl;
	cout << "   ";
	cout << setw(7) << "b ";
	for (int i = 1; i <= m; i++)
		cout << setw(7) << "X" << FJL[i];
	cout << endl;
	cout << "c ";
	for (int i = 0; i <= n; i++)
	{
		if (i >= 1)
		{
			cout << "X" << JL[i];
		}
		for (int j = 0; j <= m; j++)
			cout << setw(7) << kernel[i][j] << " ";
		cout << endl;
	}
}
void DCXA()
{
	float max1, max2, min;
	int e = -1, k = -1;
	while (1)
	{
		max1 = 0; max2 = 0;
		min = 99999999.9;
		//入基变量
		for (int i = 1; i <= m; i++)
		{
			if (max1 < kernel[0][i])
			{
				max1 = kernel[0][i];
				e = i;
			}
		}
		if (max1 <= 0)
		{
			cout << endl;
			cout << "获得最优解: " << kernel[0][0] << endl;
			break;
		}
		for (int i = 1; i <= n; i++)
		{
			if (max2 < kernel[i][e])
			{
				max2 = kernel[i][e];
			}
			float temp = kernel[i][0] / kernel[i][e];
			if (temp > 0 && temp < min)
			{
				min = temp;
				k = i;
			}
		}
		cout << "入基变量" << "X" << FJL[e] << " ";
		cout << "离基变量" << "X" << JL[k] << " ";
		if (max2 == 0)
		{
			cout << "解无届" << endl;
			break;
		}
		//变基变换
		char temp = FJL[e];
		FJL[e] = JL[k];
		JL[k] = temp;
		//更新非入基列和非离基行的所有位置的元素
		for (int i = 0; i <= n; i++)
		{
			if (i != k)
			{
				for (int j = 0; j <= m; j++)
				{
					if (j != e)
					{
						if (i == 0 && j == 0)
							kernel[i][j] = kernel[i][j] + kernel[i][e] * kernel[k][j] / kernel[k][e];
						else
							kernel[i][j] = kernel[i][j] - kernel[i][e] * kernel[k][j] / kernel[k][e];
					}
				}
			}
		}
		//更新入基列元素(不包括交叉为)
		for (int i = 0; i <= n; i++)
		{
			if (i != k)
			{
				kernel[i][e] = -kernel[i][e] / kernel[k][e];
			}
		}
		//更新离基行元素(不包括交叉位)
		for (int i = 0; i <= m; i++)
		{
			if (i != e)
			{
				kernel[k][i] = kernel[k][i] / kernel[k][e];
			}
		}
		kernel[k][e] = 1 / kernel[k][e];
		print();
	}
}
int main()
{
	//非基本变量个数和非基本变量下标
	cin >> m;
	for (int i = 1; i <= m; i++)
		cin >> FJL[i];
	//基本变量个数和基本变量下标
	cin >> n;
	//标准型初始单纯形表参数
	for (int i = 1; i <= n; i++)
		cin >> JL[i];
	for (int i = 0; i <= n; i++)
	{
		for (int j = 0; j <= m; j++)
			cin >> kernel[i][j];
	}
	print();
	DCXA();
	return  0;
}
来源
线性规划网络流 :工厂最大效益——单纯形算法【超详+题解】_工厂线性规划-CSDN博客 

问题 G: 5.5 一山不容二虎

题目描述

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

输入

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

输出

可以摆放的方案总数
ans

样例输入 复制
1
4
样例输出 复制
2
 代码
#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 集合合并

题目描述

给定两个集合A,B 求A∪B 

输入

输入有多组(不定)的数据
输入每组数据三行
每组第一行,两个整数,n,m (0<n,m≤10 000)
第二行 n个整数 代表集合A中的数字 (在int范围内)
第三行 m个整数 代表集合B中的数字 (在int范围内)

输出

输出每组数据输出在一行,代表合并后的集合的每个元素,要求从小到大输出

样例输入 复制
1 2
1
2 3
1 2
1
1 2
样例输出 复制
1 2 3
1 2
代码
#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;
}

 问题 I: 2.4.8.2 并行处理

题目描述

并行处理中的一种编程范式是生产者/消费者范式,可以用一个带有 "管理者 "进程和若干 "客户 "进程的系统来实现。客户可以是生产者,也可以是消费者,等等。管理者保持客户进程的跟踪。每个进程都由其成本来识别,成本是一个严格的正整数,范围为1 ... 10000。 具有相同成本的进程的数量不能超过10000。队列根据三种类型的请求进行管理,如下所示。

 a x - 向队列中添加成本为x的进程。

 r - 如果可能的话,根据当前管理器的策略,从队列中删除一个进程。

 p i--执行管理器的政策i,其中i为1或2。默认的管理器策略是1 e - 结束请求列表。

有两个管理器策略。

1 - 移除最小成本过程 2 - 删除最大成本过程

只有当被删除的进程的序号在删除列表中时,管理器才会打印出被删除进程的成本。 你的工作是编写一个程序来模拟管理器的过程。

输入

输入是来自标准输入。输入的每个数据集有以下格式。 进程的最大成本 移除列表的长度 删除列表--将显示被删除进程的序号列表;例如,1 4意味着将显示第一和第四个被删除进程的成本 请求列表,每个请求都在一个单独的行中。

每个数据集以一个e请求结束。数据集之间用空行隔开。

输出

程序在标准输出上打印出每个被移除的进程的成本,前提是移除请求的序号在列表中,并且此时队列不是空的。如果队列是空的,程序会打印出-1。这些结果被打印在不同的行上。一个空行将不同数据集的结果分开。 下面给出一个例子。

样例输入 复制
5
2
1 3
a 2
a 3
r
a 4
p 2
r
a 5
r
e
样例输出 复制
2
5

 问题 J: 2.4.9.1 硬木种类

题目描述

某国有数百种硬木树种,该国自然资源部利用卫星成像技术编制了一份特定日期每棵树的物种清单。计算每个物种占所有种群的百分比。

输入

输入包括每棵树的物种清单,每行一棵树。物种名称不超过30个字符,不超过10000种,不超过1000000棵树。

输出

按字母顺序输出植物种群中代表的每个物种的名称,然后使占所有种群的百分比,保留小数点后4位。

样例输入 复制
Red Alder
Ash
Aspen
Basswood
Ash
Beech
Yellow Birch
Ash
Cherry
Cottonwood
Ash
Cypress
Red Elm
Gum
Hackberry
White Oak
Hickory
Pecan
Hard Maple
White Oak
Soft Maple
Red Oak
Red Oak
White Oak
Poplan
Sassafras
Sycamore
Black Walnut
Willow
样例输出 复制
Ash 13.7931
Aspen 3.4483
Basswood 3.4483
Beech 3.4483
Black Walnut 3, 4483
Cherry 3.4483
Cottonwood  3.4483
Cypress 3.4483
Gum 3.4483
Hackberry 3.4483
Hard Maple 3.4483
Hickory 3.4483
Pecan 3.4483
Poplan 3.4483
Red Alder 3.4483
Red Elm 3.4483
Red Oak 6.8966
Sassafras 3.4483
Soft Maple 3.4483
Sycamore 3.4483
White Oak 10.3448
Willow 3.4483
Yellow Birch 3.4483
 代码
#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
提示

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

代码
#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)

 代码
#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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值