最大效益
属于二维数组的内容
题目
-
问题描述
明明的爸爸开了一家小公司,公司里有5名职员。今天,公司接待了5位客户。明明的爸爸知道,和任何一位客户谈判并签下合同都要花一整天的时间,而他又希望在一天之内,和这5位客户都签好合同。因此,明明的爸爸要求公司里的5名职员分别与1位客户谈判。
明明的爸爸也知道,这5名职员和5位客户的性格各不相同。因此,不同的职员与不同的客户谈判,会给公司带来不同的经济效益。他现在要做出一个决策,让5名职员分别与哪位客户谈判,才能让公司今天的总经济效益最大。
明明的爸爸首先做了一张5行5列的效益表,如下所示:
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
在这张效益表中,每行代表一名公司职员,每列代表一个客户,每行中的5个数字就表示了当该行所代表的公司职员和每位客户谈判时所能产生的效益。明明的爸爸就要通过这张效益表来决定哪位职员与哪位顾客谈判,然后能够使公司的效益最大。就拿上面这张表来看,由于无论哪位职员与哪位客户谈判,所产生的效益都是1,因此最大的效益就是5。这是最简单的一种情况,但是当效益表里的数字变得复杂,就很难进行选择,到底哪种组合方式才是最优的。因此明明的爸爸求助于你,帮助他解决这个问题。
明明的爸爸的问题可以归结为:给你一张5行5列的效益表,表中的数字均为大于等于0的整数,要求在这张表中选出5个数字,使这5个数字的和最大。(注:这5个数字分别来自表中的不同行不同列,即同一行只能选择一个数字,同一列也只能选择一个数字。) -
输入说明
你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据。每组测试数据占5行;每行包含5个正整数;第i行的第j个正整数Aij代表第i名职员与第j位客户谈判能为公司带来的经济效益(0≤Aij≤100, 1≤i,j≤5)。每组测试数据与其后一组测试数据之间没有任何空行;第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。 -
输出说明
对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将每组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。每组运算结果为一个整数s,即这一天中公司的最大可能总经济效益。例如:当测试数据中的所有Aij(1≤i,j≤5)均为1时,运算结果s应为5。输出时,每组运算结果s单独占一行,其行首和行尾都没有任何空格或其他任何字符;每组运算结果与其后一组运算结果之间没有任何空行或其他任何字符,第一组运算结果前面以及最后一组运算结果后面也都没有任何空行或其他任何字符。
注:通常,显示屏为标准输出设备。 -
输入范例
98 97 96 95 94
1 2 3 4 5
11 22 33 44 55
66 77 88 99 1
13 32 64 7 86 -
输出范例
318
解题思路
- 本题思路是将问题转化为列的全排列问题,因为每一行必须选一个数且列不能重复,可以用一个长度为 5 的数组表示第 i 行选择的列号,然后枚举 0~4 的所有排列(共 5! = 120 种),对于每一种排列计算总效益,即第 i 行取第 col[i] 列的值并求和,不断更新最大值即可,由于规模很小,直接用 next_permutation 枚举即可。
-next_permutation(col, col + 5) 是 C++ 标准库 里的一个函数,用来生成"下一个字典序排列"。
它的作用是:
把区间 [col, col+5) 里的元素重新排列成"当前排列的下一个更大的排列",如果已经是最大排列(比如 4 3 2 1 0),就返回 false,否则返回 true。
简单理解就是:
给你一个数组,它会自动帮你按字典序一个一个往后排。
例如:
int col[3] = {0,1,2};
排列顺序会是:
0 1 2
0 2 1
1 0 2
1 2 0
2 0 1
2 1 0
当变成 2 1 0 再调用一次 next_permutation,它会返回 false,表示已经没有更大的排列了。
整体代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[5][5];
while(true){
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
if(!(cin>>a[i][j])){
return 0;
}
}
}
int col[5]={0,1,2,3,4};
int maxsum=0;
int sum=0;
for(int i=0;i<5;i++){
sum+=a[i][col[i]];
}
maxsum=sum;
while(next_permutation(col,col+5)){
sum=0;
for(int i=0;i<5;i++){
sum+=a[i][col[i]];
}
if(sum>maxsum){
maxsum=sum;
}
}
cout<<maxsum<<endl;
}
return 0;
}
注意事项
- 注意怎么判断一组数据的输入。
螺旋方阵
属于二维数组的内容
题目
-
问题描述
明明在上学的时候,参加数学兴趣班。在班上,老师介绍了一种非常有趣的方阵,称之为螺旋方阵。该方阵一共由n×n个正整数构成(我们称之为n阶螺旋方阵),即共有n行n列。
方阵中的数字从1开始递增,数字的排序规则是从左上角出发由1开始排序,并按顺时针方向旋进,即先排最外面的一圈,然后排里面的一圈,以此类推,直到排到最后一个数为止。
例如一个4阶的螺旋方阵,一共有4×4=16个正整数构成,数字从1递增到16,最后排出来的方阵如下:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
明明回家后想自己动手构造这样的螺旋方阵。他从n=1开始构造,但是他发现当n越来越大时,螺旋方阵的复杂性就越高,然后构造出来的方阵就越容易出错。为了降低构造方阵的出错率,提高构造速度,明明就求助于你,请你帮他写一个程序,来构造螺旋方阵。 明明的问题可以归结为:给你一个正整数n,请你按题目描述中所述的方法,构造出n阶的螺旋方阵。 -
输入说明
你写的程序要求从标准输入设备中读入测试数据作为你所写程序的输入数据。标准输入设备中有多组测试数据,每组测试数据仅占一行,每行仅有一个正整数n(1≤n≤10),即所要构造的螺旋方阵的阶数。每组测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。 -
输出说明
对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将这一组运算结果作为你所写程序的输出数据依次写入到标准输出设备中。每组运算结果为一个n阶的螺旋方阵,方阵中的数字用一个空格隔开,具体形式请参考输出样例。每组运算结果与其后一组运算结果之间有一个空行,最后一组运算结果之后没有空行。 注:通常,显示屏为标准输出设备。 -
输入范例
9
4 -
输出范例
1 2 3 4 5 6 7 8 9
32 33 34 35 36 37 38 39 10
31 56 57 58 59 60 61 40 11
30 55 72 73 74 75 62 41 12
29 54 71 80 81 76 63 42 13
28 53 70 79 78 77 64 43 14
27 52 69 68 67 66 65 44 15
26 51 50 49 48 47 46 45 16
25 24 23 22 21 20 19 18 171 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
解题思路
- 本题思路是利用"分层控制"的方法构造螺旋方阵,定义四个边界变量 top、bottom、left、right 分别表示当前未填充区域的上、下、左、右边界,然后用一个变量 num 从 1 开始递增填数,在 while(top<=bottom && left<=right) 循环中按顺时针方向依次完成四个步骤:先在 top 行从 left 到 right 填充(从左到右),填完后 top++;再在 right 列从 top 到 bottom 填充(从上到下),填完后 right--;如果仍满足行边界条件,则在 bottom 行从 right 到 left 填充(从右到左),填完后 bottom--;如果仍满足列边界条件,则在 left 列从 bottom 到 top 填充(从下到上),填完后 left++;每完成一圈边界就向内收缩一层,直到所有元素填完为止,最后按矩阵形式输出即可。
整体代码
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
bool first=true;
while(cin>>n){
if(!first){
cout<<endl;
}
first=false;
vector<vector<int> > a(n,vector<int>(n,0));
int top=0,bottom=n-1;
int left=0,right=n-1;
int num=1;
while(top<=bottom&&left<=right){
//从左到右
for(int i=left;i<=right;i++){
a[top][i]=num++;
}
top++;
//从上到下
for(int j=top;j<=bottom;j++){
a[j][right]=num++;
}
right--;
//从右到左
if(top<=bottom){
for(int i=right;i>=left;i--){
a[bottom][i]=num++;
}
bottom--;
}
//从上到下
if(left<=right){
for(int j=bottom;j>=top;j--){
a[j][left]=num++;
}
left++;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(j>0){
cout<<" ";
}
cout<<a[i][j];
}
cout<<endl;
}
}
return 0;
}
注意事项
- 注意每次循环的边界和变量的更新。
方块转换
属于二维数组的内容
题目
- 问题描述
一块N x N(1=<N<=10)正方形的黑白瓦片的图案要被转换成新的正方形图案。
写一个程序来找出将原始图案按照以下列转换方法转换成新图案的最小方式:
#1:转90度:图案按顺时针转90度。
#2:转180度:图案按顺时针转180度。
#3:转270度:图案按顺时针转270度。
#4:反射:图案在水平方向翻转(形成原图案的镜像)。
#5:组合:图案在水平方向翻转,然后按照#1-#3之一转换。
#6:不改变:原图案不改变。
#7:无效转换:无法用以上方法得到新图案。
如果有多种可用的转换方法,请选择序号最小的那个。
比如:
转换前:
@-@
@@-
转换后:
@-@
@--
--@
这种转换采取#1(按顺时针转90度)即可。
注意:图案中的字符"@"和"-"在转90度后,还是"@"和"-"。不要认为"-"转90度后变成"|"。
-
输入说明
第一行: 单独的一个整数N。
第二行到第N+1行: N行,每行N个字符(不是'@'就是'-');这是转换前的正方形。
第N+2行到第2*N+1行: N行,每行N个字符(不是'@'就是'-');这是转换后的正方形。 -
输出说明
单独的一行包括1到7之间的一个数字(在上文已描述)表明需要将转换前的正方形变为转换后的正方形的转换方法。 -
输入范例
3
--@
--@@@-
-
输出范例
3
解题思路
- 本题思路是把原图案和目标图案都存入二维字符数组,然后分别编写几个功能函数:一个顺时针旋转90度的函数(通过 b[j][n-1-i]=a[i][j] 实现坐标变换),在此基础上可以连续调用两次得到180度、三次得到270度;一个水平方向翻转函数(即按行不变、列做对称,b[i][n-1-j]=a[i][j]);再写一个比较函数判断两个二维数组是否完全相同。主程序中按照题目给定的优先顺序依次判断:先判断是否为90度旋转,如果是直接输出1;否则判断180度输出2;否则判断270度输出3;否则判断单纯反射输出4;否则先对原图做一次反射,再分别尝试旋转90、180、270,只要有一个相同输出5;如果原图本身就与目标图一致输出6;若以上都不满足则输出7。由于要求选最小编号,所以必须严格按1到7的顺序判断。
整体代码
cpp
#include<bits/stdc++.h>
using namespace std;
int n;
vector<string> rotate90(vector<string>& a){
vector<string> b(n,string(n,' '));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
b[j][n-i-1]=a[i][j];
}
}
return b;
}
vector<string> reflect(vector<string>& a){
vector<string> b(n,string(n,' '));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
b[i][n-1-j]=a[i][j];
}
}
return b;
}
bool isequal(vector<string>& a,vector<string>& b){
for(int i=0;i<n;i++){
if(a[i]!=b[i]){
return false;
}
}
return true;
}
int main(){
cin>>n;
vector<string> a(n),b(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
}
vector<string> t;
t=rotate90(a);
if(isequal(b,t)){
cout<<1<<endl;
return 0;
}
t=rotate90(t);
if(isequal(b,t)){
cout<<2<<endl;
return 0;
}
t=rotate90(t);
if(isequal(b,t)){
cout<<3<<endl;
return 0;
}
t=reflect(a);
if(isequal(b,t)){
cout<<4<<endl;
return 0;
}
vector<string> r;
r=reflect(a);
for(int i=0;i<3;i++){
r=rotate90(r);
if(isequal(b,r)){
cout<<5<<endl;
return 0;
}
}
if(isequal(a,b)){
cout<<6<<endl;
return 0;
}
cout<<7<<endl;
return 0;
}
注意事项
- 注意vector 本身就已经是一个二维结构:a[行][列]等价于vector<vector>而如果使用 vector<vector>,结构会变成:a[行][列] 是一个 string。
英文段落翻译
自己翻译
法国发明家约瑟夫玛丽雅卡尔在设计一个自动的织机中使用了细的穿孔的木质板来控制复杂的编织设计。在1880年代,美国统计学家赫尔曼何勒里斯构想了一个使用类似雅卡尔的板子的穿孔的卡片来处理数据的想法。运用一个布满电触点的穿孔卡片的系统,它就能够为1890年的美国人口普查编译统计学信息。
分析机
同样在19世纪,英国数学家和发明家查尔斯巴比奇得出了现代数据计算机的规则。他构想了一系列的机器,例如差分机,它被设计用来处理复杂的数学问题的机。许多历史学家认为,巴比奇和他的联想,以及数学家奥古斯塔艾达拜伦,是真正的现代数据计算机的先锋。巴比奇的设计之一,分析机,有许多一个现代计算机的特点。它有一个以一叠穿孔卡片的形式存在的输入流,一个保存数据的仓库,一个进行算术运算的工厂和一个产生永久性记录的打印机。虽然巴比奇没有将他的想法变为现实,但是在那时候它已经非常的技术可行了。
早期计算机
在19世纪晚期,模拟计算机开始被建造。早期的模型通过旋转的轴和齿轮的形式来计算。大量的方程式的近似值用这种机器以任何方式来估算,解决都是非常困难的。开尔文勋爵建立了一个机器的潮汐预测器,一种特殊的模拟计算机。在第一次和第二次世界大战期间,机器的以及之后的电子模拟计算系统被用作潜艇的鱼雷预测和飞行器的轰炸瞄准器的控制。另一个系统被设计用来预测密西西比河的洪水爆发。
AI翻译
法国发明家约瑟夫-马里·雅卡尔在设计自动织布机时,使用了穿孔的薄木板来控制复杂图案的编织。19世纪80年代,美国统计学家赫尔曼·何乐礼借鉴雅卡尔木板卡片的思路,构想出使用穿孔卡片处理数据的方法。他采用了一种让穿孔卡片通过电接触点的系统,成功为1890年美国人口普查完成了数据统计工作。
1 . 分析机
同样在19世纪,英国数学家兼发明家查尔斯·巴贝奇阐明了现代数字计算机的基本原理。他构思了多款机器,例如专门设计用于处理复杂数学问题的差分机。许多历史学家认为,巴贝奇及其合作者、数学家奥古斯塔·埃达·拜伦是现代数字计算机的真正先驱。巴贝奇设计的分析机中,已具备现代计算机的诸多特征:它拥有以穿孔卡片组形式存在的输入流、用于保存数据的"存储库"、执行算术运算的"工坊",以及能生成永久记录的打印机。尽管在当时这项构想或许在技术上已属可行,但巴贝奇最终未能将其付诸实践。
2 . 早期计算机
模拟计算机于19世纪末开始问世。早期的模拟计算机通过旋转的传动轴和齿轮进行计算,人们用这类机器来求解那些因过于复杂而难以用其他方法处理的方程的近似数值解。开尔文勋爵曾制造过一台机械式潮汐预测仪,这是一种专用的模拟计算机。在第一次及第二次世界大战期间,机械式及后来的电子式模拟计算系统被用作潜艇中的鱼雷航向预测器,以及飞机上的轰炸瞄准控制器。此外,还曾设计过另一套系统用于预测密西西比河流域的春季洪水。
单词打卡

下一篇
待续