程序设计艺术与方法 小组解题报告

仅供参考~
务必独立思考~
个人实验报告https://blog.csdn.net/qq_44977889/article/details/106887871

题目A 机器人足球
足球场地长为 100,宽为 20,对方的球门坐标为(100,10),你要控制一个机器人踢球,初始位置为(x,y)。机器人可以朝任何方向移动,但不能超出场地边界。当机器人与球门距离不超过 10 时,可以射门。问机器人从初始位置出发到射门,
最少要移动多少距离?(四舍五入到小数点后 3 位)

解题思路:
这道题解题的关键就是要求出机器人球员的初始位置和球门坐标的距离,再对这个距离进行判断是否需要移动,需要移动时计算出移动的距离。

具体解法:
由于球门的坐标是固定的,则可以画出球场的坐标系,(x,y)表示的是机器人球员的初始位置,从图中我们可以清楚的看到圆形区域就是球员的可射门区域,可以很直观的判断出进行踢球动作时的范围。
由于是以点为参考单位,可以先建立一个Point类。在输入球员的初始坐标后,用来初始化一个Point的对象,接着创建一个球门对象。接下来对该球员的初始位置到对方球门的距离进行计算,判断他们之间的距离d是否大于10(是否在以点(100,10)为圆心,半径为10的半圆内)。当d<=10时,则球员直接进行踢球动作,否则先使球员移动到可踢球范围内。通过对球场模型的数学分析,可以得到球员移动的最短距离即为d-10。因此,球员可以踢球需要移动的最短距离等于(x,y)到(100,10)的距离d-10。
由于题目对输出结果的精度要求为保留三位小数,所以在输出结果时要注意格式的控制。
在这里插入图片描述
代码实现:
Point类:
class Point{
public:
Point(double X, double Y) {x = X;y = y;};
~Point() {};
double x;double y;
};
主函数:
#include
#include<math.h>
#include
#include “Point.h”
using namespace std;
double distance(Point p,Point center) {
double d;//机器人球员和球门的距离
d = sqrt(pow((p.x - center.x), 2) + pow((p.y - center.y), 2));
return d;
}
int main() {
double x; double y;//机器人球员的坐标
cout << "请输入机器人球员的坐标(0<=x<=100,0<=y<=20): ";
cin >> x>> y;
Point p(x, y); Point center(100, 10);
double d = distance(p, center);
if (d <= 10)
cout << “此时机器人球员和球门的距离小于10,可以直接射门!” << endl;
else
cout << “此时机器人球员和球门的距离大于10,仍需移动:” << setprecision(3)<<fixed<<d-10 << endl;
system(“pause”);
return 0;
}

运行结果:
在这里插入图片描述
题目B 纸牌识别
Alice 沉迷于机器人研究,他打算做一个机器人来检查一副扑克是否完整。现在,他想请你帮他写一个程序,来识别纸牌。每张纸牌都有一个花色(四种花色,分 别用大写字母P,K,H,T 表示)和一个数字点数(1-13)。纸牌可以用ABC 的形式来表示,A 代表花色,BC代表数字,如果数字小于10,会有一位补0。比如花色是P,数字是9 的纸牌会表示成P09。一副完整的纸牌有52张牌,四种不同的花色各有1张数字1-13的牌。 你的程序要读入一个字符串,表示缺少的纸牌有哪些。如果包含相同的纸牌(花色数字都相同)输出GRESKA,否则输出每种花色剩余的纸牌数量。

解题思路:
对字符串进行遍历,将花色一样的纸牌放入一个容器,如果放入的时候容器中已存在,则返回GRESKA,否则放入。最后可以根据每个容器中的数量计算出每种花色剩余数量。

具体解法:
首先建立四个set容器(set容器可以实现快速查找且没有重复元素)来分别存放四种花色已出现的数字点数。然后用for循环对字符串遍历,每次遍历三个字符,根据第一个字符判断是哪种花色,再将后面两个字符转化成整型变量,判断是否在该花色容器中存在,存在返回GRESKA,不存在放入容器。遍历完成分别输出剩余数量。

代码实现:
#include
#include
#include
using namespace std;
int main(){
string str;
cout<<“Input:”;
cin>>str;
set setP;
set setK;
set setH;
set setT;
int countP=0;
int countK=0;
int countH=0;
int countT=0;
for(int i=0;i<str.size();i+=3){
if(str[i]==‘P’){
string s=str.substr(i+1,i+2);
int intStr=stoi(s);
if(setP.count(intStr)0){
setP.insert(intStr);
countP++;
}
else
cout<<“GRESKA”<<endl;
}
if(str[i]
‘K’){
string s=str.substr(i+1,i+2);
int intStr=stoi(s);
if(setK.count(intStr)==0){
setK.insert(intStr);
countK++;
}
else
cout<<“GRESKA”<<endl;
}
if(str[i] ==‘H’){
string s=str.substr(i+1,i+2);
int intStr=stoi(s);
if(setH.count(intStr)0){
setH.insert(intStr);
countH++;
}
else
cout<<“GRESKA”<<endl;
}
if(str[i]
‘T’){
string s=str.substr(i+1,i+2);
int intStr=stoi(s);
if(setT.count(intStr)==0){
setT.insert(intStr);
countT++;
}
else
cout<<“GRESKA”<<endl;
}
}
cout<<13-countP<<" “<<13-countK<<” “<<13-countH<<” "<<13-countT<<endl;
return 0;
}

运行结果:
在这里插入图片描述
题目C 卡牌对决
有2N张牌,它们的点数分别为1到2N。Alice拿了其中的N张,Bob拿了剩下的N 张。Alice和Bob会进行N 轮游戏,在每轮游戏中,Alice和Bob各出一张牌。出了的牌不能收回。在前N/2轮中,每轮谁的牌点数大谁就赢;在后N/2轮中,每轮谁的牌点数小谁就赢。已知Bob每一轮会出什么牌,试求Alice最多能赢多少轮。

解题思路:
这道题类似排列组合问题,Bob的出牌顺序是已知的,按照卡牌规则,在前N/2 轮中,每轮谁的牌点数大谁就赢;在后N/2 轮中,每轮谁的牌点数小谁就赢,对Alice的牌按照升序进行排序,然后进行比对。

具体解法:
Alice出的牌用vector1存储,Bob出的牌按照升序在vector2中存储,用一个unordered_map存储卡牌的每一次对决,key为Alice的牌,value为Bob的牌。
对Alice的前N/2的牌进行降序排列,用Alice的前N/2和Bob的后N/2张牌进行pk,通过双重for循环进行比较。用Alice的后N/2和Bob的前N/2张牌进行对比。

代码实现:
#include
#include
#include
#include <unordered_map>
using std::cin;
using std::clog;
using std::cout;
using std::endl;
using std::unordered_map;
using std::vector;
class Solution{
public:
int paxWinNumber(vector vec, int n);
};
int Solution::paxWinNumber(vector vec, int n){
int win_nup = 0;
vector bob{};
unordered_map<int, int> result{};
for (int i = 1; i < 2 * n + 1; i++){
if (std::find(vec.begin(), vec.end(), i) == vec.end()){
bob.push_back(i);
}
}
std::sort(vec.begin(), vec.begin() + n / 2);
for (int j = 0; j < n / 2; j++){
for (int k = j + n / 2; k < n; k++){
if (vec[j] < bob[k]){
result[j] = k;
break;
}
}
}
std::sort(vec.begin() + n / 2, vec.end());
for (int p = 0; p < n / 2; p++){
for (int q = p + n / 2; q < n; q++){
if (vec[q] > bob[p])
{
result[q] = p;
break;
}
}
}
return result.size();
}
int main()
{
int n = __cplusplus;
clog << "please input a even, n: " << endl;
if (!(cin >> n) || n % 2 != 0)
exit(EXIT_SUCCESS);
vector alice{};
int cart_number = 0;
clog << "please input Alice cart " << endl;
for (int i = 0; i < n; i++)
{
if (!(cin >> cart_number) || cart_number > 2 * n)
exit(EXIT_SUCCESS);

    alice.push_back(cart_number);
}

Solution solu;
cout << "most numbers to win is " << solu.paxWinNumber(alice, n) << endl;
return 0;

}

运行结果:
在这里插入图片描述

题目D自驾游
P 省有N 个城市,编号分别为1…N,烦烦家住1 号城市,N 号城市是省会。P省的交通非常发达,有M 条有向的高速公路连接这N 个城市,第i 条高速公路(1<=i<=M)从城市ui 连向城市vi。
这天,烦烦想自己开车从家前往省会城市游玩。烦烦是个做事很细致的人,为了有备无患,她决定同时开着heroMap 和amap 这两个不同的导航软件来帮助自己完成这次旅程。这两个导航软件内部采用了不同的算法,对于第i 条高速公路(1<=i<=M),heromap 认为通过时间为Pi 分钟,amap 则认为通过时间为Qi 分钟。这两个导航软件会根据自己的数据来计算从当前位置到目标位置所需的最短时间是多少,对于第i 个城市(1<=i<=N),记heromap 认为从i 到N
的最短时间为hero(i),记amap 认为i 到N 的最短时间为a(i)。烦烦开车途径某条高速公路(1<=i<=M)时,如果heromap 认为从ui 到N 不应该走这条路,即hero(vi)+Pi>hero(ui),则发出一次警告,同样的,如果amap 认为从ui 到N 不应该走这条路,即a(vi)+Qi>a(ui),也会发出一次警告。现在烦烦希望自己选择一条路径,使得受到的警告总数最少。请你编程解决这一问题。

解题思路
此题可理解为带权值有向图;耗时最短即为到达目的地的最短路径,通过Dijkstra算法确定不同软件的最短路径,然后求出两条不同的最短路径发出警报次数,最后选取发出警报次数少的路径。通过题目条件hero(vi)+Pi>hero(ui) 和 a(vi)+Qi>a(ui)再次运用Dijkstra算法求出两条不同的最短路径发出警报次数

具体解法:
声明一个结构体node用于表示城市ui高速公路所连接的下一城市vi以及之间的距离。分别用Dijkstra算法求出使用两个不同导航软件时的最短路径并将距离分别存入两个不同数组。比较两个路径的距离,选取最短的一个。再用Dijkstra算法判断该路线中每个节点用另一个导航软件到终点的最短路径中的下一节点是否相同,若不相同则警报次数加一。

代码实现:
#include ;
#include ;
#include ;
using namespace std;
const int maxn = 100;
const int maxm = 100;
const int inf = 999999;
int N, M;//点 边

struct edge {
int amap;
int hero;
int next;
int to;
};

struct new_edge {
int to;
int dis;
};

edge e[maxm];

vector<new_edge> v[maxn];

int cnt, head[maxn], dist_1[maxn], dist_2[maxn], dist_3[maxn];
bool mark[maxn]; //标记点

inline void add_edge(int ui, int vi, int Pi, int Qi) { //内联函数
cnt++;
e[cnt].to = ui;
e[cnt].hero = Pi;
e[cnt].amap = Qi;
e[cnt].next = head[vi];
head[vi] = cnt;
}

struct node {
int dis;
int pos;
bool operator <(const node& x)const { //运算符重载函数
return x.dis < dis;
}
};
priority_queueq; //优先队列,在队列基础上添加了一个内部排序

void dij1(int s) {
fill(dist_1, dist_1 + maxn, inf);//容器填充:将inf从d1开始填充至d1+maxn;距离初始化为inf
dist_1[s] = 0;//本身距离为0
q.push(node{ 0, s }); //将最短距离和点存入队列
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.pos;//点
mark[x] = true; //标记
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (dist_1[y] > dist_1[x] + e[i].hero) {
dist_1[y] = dist_1[x] + e[i].hero;
if (!mark[y]) {
q.push(node{ dist_1[y], y });
}
}
}
}
}

void dij2(int s) {
fill(mark, mark + maxn, false);
fill(dist_2, dist_2 + maxn, inf);
dist_2[s] = 0;
q.push(node{ 0, s });
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.pos;
mark[x] = true;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (dist_2[y] > dist_2[x] + e[i].amap) {
dist_2[y] = dist_2[x] + e[i].amap;
if (!mark[y]) {
q.push(node{ dist_2[y], y });
}
}
}
}
}

void dij3(int s) {
fill(mark, mark + maxn, false);
fill(dist_3, dist_3 + maxn, inf);
dist_3[s] = 0;
q.push(node{ 0, s });
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.pos;
mark[x] = true;
for (int i = 0; i < v[x].size(); i++) {
new_edge tmp2 = v[x][i];
int y = tmp2.to;
if (dist_3[y] > dist_3[x] + tmp2.dis) {
dist_3[y] = dist_3[x] + tmp2.dis;
if (!mark[y]) {
q.push(node{ dist_3[y], y });
}
}
}
}
}

int main() {
cin >> N >> M;//点 边
for (int i = 1; i <= M; i++) {
int ui, vi, Pi, Qi;
cin >> ui >> vi >> Pi >> Qi;
add_edge(ui, vi, Pi, Qi);
}
dij1(N);//找到最短路径,将节点存入队列
dij2(N);
for (int i = 1; i <= N; i++) {
for (int j = head[i]; j; j = e[j].next) {
new_edge tmp;
tmp.dis = 0;
if (dist_1[i] + e[j].hero > dist_1[e[j].to])tmp.dis++;
if (dist_2[i] + e[j].amap > dist_2[e[j].to])tmp.dis++;
tmp.to = i;
v[e[j].to].push_back(tmp);
}
}
dij3(1);
cout << dist_3[N];
return 0;
}
运行结果:
在这里插入图片描述
题目E现代艺术
给出平面上N个点的坐标点集,求这N个点有多少条整体对称轴。整体对称轴是指一条直线,对于每个点,都能找到点集中的一个点 与他关于这条直线对称,求对称轴数量

解题思路
利用找凸包的方法,找到点集的凸包。获得点集的中心点,横坐标为所有点的横坐标之和的平均数,纵坐标为所有点纵坐标之和的平均数。以点集的中心点和凸包中每相邻两点的中点连成的直线作为对称轴,判断该对称轴和这相邻两点的连线是否垂直,垂直则说明该对称轴就是点集的整体对称轴,否则不是点集的整体对称轴。

具体解法:
创建一个Point类型的数组p[],用来存储点集,并在输入点集数据的时候计算得到中心点center的坐标,为了方便凸包的寻找,将点集中的数据进行排序,这里用到的排序算法中的比较函数,是自定义的turn函数(根据叉积来进行排序),将以上操作完成之后就可以进行凸包的寻找。
在寻找凸包时,先建立一个Point类型的数组temp[],用来储存凸包中的点。利用扫描法,根据角度判断是否入栈,进而得到凸包。经过上面操作后,temp[]数组中的点全部是凸包中的点。将凸包中的点两两组合,以每两点的中点与中心点center之间的连线为轴,如果该轴为这两点连线的中垂线,则说明该轴为点集的整体对称轴,反之则不是。由于点的组合可能会重复,如(p1,p2)与(p2,p1),导致求出的对称轴可能会重复,这时候利用set容器的元素不可重复性,将对称轴的斜率存入set容器中,得到的set容器的size就是整体对称轴的条数。

代码实现:
Point类:
class Point{
public:
Point() {};
Point(int X, int Y) { x = X; y = Y; }
~Point() {};
int x;int y;
};
主函数:
#include
#include
#include
#include"Point.h"
#define MAX 1000
#define maxK double(1e8)//表示斜率不存在时的情况
Point p[MAX];
Point temp[MAX];
using namespace std;
set k;
/求两个点之间的距离/
double getDistance(Point a, Point b) {
return sqrt(pow((a.x - b.x), 2) + pow((a.y - b.y), 2));
}
/求两个向量的叉积,如果为正则为逆时针方向,如果为负则为顺时针方向,在这里a为两个向量的公共点/
double getCross(Point a, Point b, Point c) {
int x1 = b.x - a.x;
int y1 = b.y - a.y;
int x2 = c.x - a.x;
int y2 = c.y - a.y;
return x1y2- y1x2;
}
/求两个点连成直线的斜率,需要分三种情况讨论/
double getK(Point a, Point b) {
if (a.y == b.y)//平行于x轴,斜率为0
return 0;
else if (a.x == b.x)//垂直于x轴,斜率不存在
return maxK;//这里用一个很大的值表示斜率不存在的情况
else
return (b.y - a.y) / (b.x - a.x);
}
/在对点集排序时将会用到的一个函数,根据叉积来判断排序的方式/
bool test(Point a, Point b) {
bool result= getCross(p[0], a, b);
if (result == 0)
return getDistance(a, p[0]) < getDistance(b, p[0]);
else
return getCross(p[0], a, b) > 0;
}
/将点按照纵坐标由低到高排序,当纵坐标相同时比较横坐标,根据test进行排序/
void turn(int N) {
for (int i = 1; i < N; i++) {
if ((p[i].y < p[0].y))
swap(p[i], p[0]);
else if (p[i].y == p[0].y && p[i].x < p[0].x)
swap(p[i], p[0]);
}
sort(p + 1, p + N, test);
}
int main() {
int N;
cin >> N;
/求凸包时用到的中心点,中心点的位置用所有点的位置的平均值表示/
Point center;
center.x = 0; center.y = 0;
for (int i = 0; i < N; i++) {
cin >> p[i].x >> p[i].y;
center.x = (center.x + p[i].x);
center.y = (center.y + p[i].y);
}
center.x=center.x/N;center.y=center.y/N;//中心点的坐标
turn(N);//根据位置信息对点集进行排序
/将符合条件的点放入栈中,定义一个数组用来存储凸包中的元素,num是栈顶元素的序号/
temp[0] = p[0]; temp[1] = p[1];
int num = 1;
for (int i = 2; i < N; i++) {
while (getCross(temp[num - 1], temp[num], p[i]) <= 0 && num >=1)//角拐向左侧,该点就不符合条件
num–;//弹栈
num++;//压栈
temp[num] = p[i];
}
//经过以上操作以后temp数组中的点全部是凸包中的点
for (int i = 0; i <= num; i++) {
for (int j = i + 1; j <= num; j++) {
Point point;//用来存储凸包中两个元素的中点
point.x = (temp[i].x + temp[j].x) / 2;
point.y = (temp[i].y + temp[j].y) / 2;
/此时由数学知识可得,经过中心点的凸包上的中垂线即为整体对称轴/
if ( (temp[i].x - temp[j].x)* (point.x - center.x) + (temp[i].y - temp[j].y) (point.y - center.y) ==0){
if (point.x == center.x && point.y == center.y)
k.insert(getK(temp[i], temp[j]));
else {
k.insert(getK(point, center));
}
}
}
}
if (k.size() == 1)
cout << k.size() + 1 << endl;
else
cout << k.size() << endl;
system(“pause”);
return 0;
}
在这里插入图片描述
题目F 领家割草
邻居Alice家有一块大草坪,每隔一段时间他都要用割草机修剪草坪;可以把草坪看成是一个N
M的矩阵,割草时需要N 台割草机水平方向穿过草地,M台割草机垂直方向穿过草地。草地并不是完全平整的,有高有低;如图所示,高的地方用深色表示,矮的地方用浅色表示。割草机工作时需要消耗燃油,在走过不同高度的草地时,会消耗A元燃油燃油费;(比如从低的地方走到高的地方,从高的地方走到低的地方,在相同高度的地块上运行割草机的燃油消耗可以忽略)。Alice 为了节省燃油费,准备改造一些地形;可以给一些地块加土来升高地形,或者把高的地方铲平来降低地形高度。对一个地块进行改造要花费B 元。你能帮邻居Alice设计一个方案。让他花费最小吗?

解题思路:
可以通过最大流最小割算法,但我是通过循环对每一块地的费用进行比较,选择一个花费最少的策略,然后再次循环,直到没有土地改变状态为止。

具体解法:
用一个vector<vector> grass_map存储原始地图,然后对每一块土地进行遍历,比较土地的两种策略所需的费用,那一个费用少就使用那一种策略,把所有的土地遍历完成之后,再次遍历,直到没有土地更改为止,这样就防止了局部最优而不一定全局最优的缺陷。

代码实现:
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::vector;

int n = __cplusplus;
int m = __cplusplus;
int a = 0;
int b = 0;
bool is_end = false;

class Solution
{
public:
vector<vector> MinMap(vector<vector> grass);
int OnePointPrice(vector<vector> &grass, int row = 0, int column = 0, bool change = false);
int MinPrice(vector<vector> &end_grass, vector<vector> &grass);
};

vector<vector> Solution::MinMap(vector<vector> grass)
{

is_end = true;

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        if (OnePointPrice(grass, i, j, false) > OnePointPrice(grass, i, j, true))
        {
            is_end = false;
            if (grass[i][j] == '#')
                grass[i][j] = '.';
            else
            {
                grass[i][j] = '#';
            }
        }
    }
}
return grass;

}

int Solution::OnePointPrice(vector<vector> &grass, int row, int column, bool change)
{
int price = 0;
if (!change)
{
if (row > 0)
{
if (grass[row][column] != grass[row - 1][column])
price = price + a;
}
if (row < n - 1)
{
if (grass[row][column] != grass[row + 1][column])
price = price + a;
}
if (column > 0)
{
if (grass[row][column] != grass[row][column - 1])
price = price + a;
}
if (column < m - 1)
{
if (grass[row][column] != grass[row][column + 1])
price = price + a;
}
}
else
{
price += b;

    if (row > 0)
    {
        if (grass[row][column] == grass[row - 1][column])
            price = price + a;
    }
    if (row < n - 1)
    {
        if (grass[row][column] == grass[row + 1][column])
            price = price + a;
    }
    if (column > 0)
    {
        if (grass[row][column] == grass[row][column - 1])
            price = price + a;
    }
    if (column < m - 1)
    {
        if (grass[row][column] == grass[row][column + 1])
            price = price + a;
    }
}

return price;

}

int Solution::MinPrice(vector<vector> &end_grass, vector<vector> &begin_grass)
{
int min_price = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (end_grass[i][j] != begin_grass[i][j])
{
min_price += b;
}
if (i > 0)
{
if (end_grass[i][j] != end_grass[i - 1][j])
min_price += a;
}
if (j > 0)
{
if (end_grass[i][j] != end_grass[i][j - 1])
min_price += a;
}
}
}

return min_price;

}
int main()
{
if (!(cin >> n >> m >> a >> b))
{
std::clog << “input isn’t suitable” << endl;
exit(EXIT_SUCCESS);
}
vector<vector> grass_map(n);
for (int i = 0; i < grass_map.size(); i++)
grass_map[i].resize(m);

char c = ' ';
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        cin >> c;
        grass_map[i][j] = c;
    }
}
Solution solu;
auto result = grass_map;
while (!is_end)
{
    result = solu.MinMap(result);
}
cout << endl
     << solu.MinPrice(result, grass_map) << endl;
return 0;

}

运行结果:
在这里插入图片描述

题目 G 括号序列
括号序列是指由‘(’和‘)’组成的序列,假如一个括号序列中,包含相同数量的左括 号和右括号,并且对于每一个右括号,在他的左侧都有左括号和他匹配,则这个 括号序列就是一个合法括号序列。比如(())()就是一个合法括号序列,但 (())(()不是合法括号序列。 给出两个长度相同的合法括号序列A 和B,那么A < B当且仅当:A 和B至少有一位不相同。在A 和B从左往右数第一个不相同的位置i,A[i]=(,B[i]=) 比如A = (())(),B = ()()(),则A < B,因为从左边数第一个不相同的 是第二个字符,A[2] = (,B[2] = )。对于长度 N,由于定义了小于关系, 则可以通过这个关系推出所有长度为 N 的合法括号序列的大小关系,对于长度 为6的合法括号序列,从小到大排列顺序如下: 1.((())) 2.(()()) 3.(())() 4.()(()) 5.()()() 给出N和M,求长度为N的合法括号序列中,第M 小的合法括号序列是?

解题思路
第一步是根据合法序列的定义将所有的合法序列找出来放入容器中,然后将容器中的元素冒泡排序,直到找到第M小的元素。

具体解法:
首先创建一个string类型的vector容器,来存放所有合法序列,然后写一个fun函数将‘(’和‘)’全排列,并将其中合法序列push到vector中。最后写一个compare函数对vector中元素排序找到第M小的元素。

代码实现:
#include
#include
#include
using namespace std;
void fun(int tag,string result,int count1,int count2,int N,vector &all)
{//该函数用来找到所有的合法序列,并放到vector中
int nexttag=tag+1;
for(int i=1;i<=2;i++){
if(i1)//1表示当前result[tag]是(
{
if(count1
N/2)
continue;
else{
string s=result;
s.push_back(’(’);
int count_1=count1+1;
if(nexttagN)
all.push_back(s);
else
fun(nexttag,s,count_1,count2,N,all);//递归调用
}
}
if(i
2)//1表示当前result[tag]是){
if(count1<=count2)
continue;
else {
string s=result;
s.push_back(’)’);
int count_2=count2+1;
if(nexttag==N)
all.push_back(s);
else
fun(nexttag,s,count1,count_2,N,all);
}
}
}
}
void compare(vector all,int M){
//该函数用来找到第M小的序列
//可以直接用AscaII顺序
for(int i=0;i<M;i++){
int min=all.size()-1;
for(int j=all.size()-2;j>=i;j–){
if(all[min]<all[j]){
string temp=all[j];
all[j]=all[min];
all[min]=temp;
}
}
}
cout<<all[M-1]<<endl;
}
int main(){
vector all;//用来存放合法序列
int N,M;
cin>>N>>M;
string result;//一种可能序列
result.reserve(N);
result.push_back(’(’);
int count1=1;//计数(已用的个数
int count2=0;//计数)已用的个数
fun(1,result,count1,count2,N,all);
compare(all,M);
return 0;
}

运行结果:
在这里插入图片描述
题目 H 不要回文
给出一个字符串S,你需要尽可能少的修改S 中的字符,使得S 不包含长度大于等于2 的回文子串。

解题思路
通过第i和i+1以及第i和i+2是否相等判断是否存在回文;存在则修改并计数

具体解法:
将字符串转化成整形并存储于数组中,首先通过temp[i] == temp[i + 2]判断相邻三个字符是否存在回文,若存在则通过temp[i] == temp[i - 1]判断修改第一个还是修改最后一个字符;通过temp[i] == temp[i + 1]判断两两相邻是否存在回文,若存在则修改第一个。遍历数组。
代码实现:
#include
using namespace std;
int temp[30], num = 0;//计数

int t = 26;//修改字符
string s;
int main() {
cin >> s;
int len = s.length();
//字符串转化成整形并存储于数组中
for (int i = 0; i < len; i++) {
temp[i + 1] = s[i] - ‘a’ + 1;
}
for (int i = 1; i <= len; i++) {
//判断相邻三个字符是否存在回文
if (temp[i] == temp[i + 2]) {
//判断修改第一个还是最后一个
if (temp[i] == temp[i - 1]) {
temp[i] = t++;
num++;
}
else
temp[i + 2] = t++, num++;
}
}
//判断两两相邻是否存在回文
for (int i = 1; i <= len; i++) {
if (temp[i] == temp[i + 1]) {
temp[i + 1] = t++;
num++;
}
}
cout << num;
return 0;
}
运行结果:
在这里插入图片描述
题目I 你的名字
Alice想要计算他那N只猫的名字的价值.每只猫的名字由不超过1000个大小写字母构成,没有一个名字是空字体串。Alice 有一张“价值字符串表”,上面有M个代表价值的字符串.每个字符串由不超过30个大小写字母构成,同样不存在空字符串。一个猫的名字蕴含多少个价值字符串,这个名字就有多少价值。所谓“蕴含”,是指某个能量字符串的所有字符都在名字串中按顺序出现(不一定一个紧接着一个)。所有的大写字母和小写字母都是等价的。比如,在贝茜的名字“Bessie”里,蕴含 有“Be”“si”“EE”以及“Es”等等字符串,但不蕴含“Ls”或“eB”。请帮Alice 计算他的猫的名字的价值。

解题思路:
由于在比较时不考虑大小写的区别,则将猫的名字和价值字符串先转化为小写再进行比较。比较时采用传统的方法,将价值字符串和每只猫的名字进行匹配,匹配成功就做一个标记。

具体解法:
先创建catName、valueString来存储猫的名字和价值字符串,输入的第一行是两个整数 N(N 行,每行一个字符串表示猫的名字)、M (M 行,每行一个价值字符串 ),然后输入猫的名字(每个名字全部转化为小写之后再存入容器catName内),输入价值字符串(每个价值字符串转化为小写之后再存入容器valueString内)。
要判断是否蕴含价值字符串,就要从每个价值字符串的第一个字符开始,从前往后单个字母的比对,每找到一个,count++,再继续判断下一个字符,这个字符串判断完毕后,最后判断count的值和该价值字符串的长度是否相等,相等则蕴含该价值字符串,不相等则不蕴含。如此即可计算出猫的名字的价值。

代码实现:
#include
#include
#include
#include
using namespace std;
vectorcatName;//猫的名字
vectorvalueString;//价值表
vector::iterator it;
vector::iterator iter;
int main() {
int N;//这N行,每行一个字符串用来表示猫的名字
int M;//这M行,每行一个价值字符串
cin >> N >> M;
int *value= new int[N];//用来存储每只猫名字中价值字符串的个数
/输入猫的名字/
for (int i = 0; i < N; i++) {
string name;
cin >> name;
/由于在比较价值字符串时,不区分大小写,因此将猫的名字全部转化为大小或者小写,方便进行比较,此处转化为小写/
transform(name.begin(), name.end(), name.begin(), ::tolower);//这里用到了STL中的一个模板函数
value[i] = 0;
catName.push_back(name);
}
/输入价值字符串/
for (int i = 0; i < M; i++) {
string value;
cin >> value;
transform(value.begin(), value.end(), value.begin(), ::tolower);
valueString.push_back(value);
}
/接下来进行字符串的匹配/
int num = 0;//用于方便记录猫的价值数组的下标
for (it = catName.begin(); it != catName.end(); it++) {
for (iter = valueString.begin(); iter != valueString.end(); iter++) {
/要判断是否蕴含价值字符串,就要从价值字符串的第一个字符开始,从前往后单个字母的比对,每找到一个,
count++,最后判断count的值和该价值字符串的长度是否相等,相等则蕴含该价值字符串,不相等则不蕴含
/
int count=0;
for (int i = 0; i < (*iter).length();i++ ) {
for (int j = 0; j < (*it).length(); j++) {
if ((*it).substr(j, 1) == (*iter).substr(i, 1)) {//这个字符匹配成功
count++;
if (count == (*iter).length())
value[num]++;
i++;//下一个字符
}
}
}
}
num++;
}
/输出每只猫名字蕴含多少个价值字符串/
for (int i = 0; i < N; i++)
cout << value[i] << endl;
system(“pause”);
return 0;
}

运行结果:
在这里插入图片描述

题目J 密信
Alice 想给Bob发短信,短信的内容可以看成是一个只有小写字母的字符串p; 为了加密短信,Alice需要只有小写字母长度为n的字符串h,并且p是h的子 串;Alice 想知道,这样的字符串有多少种。给出n和M还有字符串p,假设一共有K种不同的h,输出K mod M。

解题思路:
第一步将26个字母以给定的n位全排列,找到所有n位字符的字符串。第二步,对这些字符串进行判断,如果字符串中包含字符串ab则计数。最后可以得到所有h的个数。

具体解法:
首先用while循环分别测试每组,count表示h的个数,adress函数是n位字符的所有可能的字符串,具体就是遍历26个字母,来表示第tag位可能的字符,然后递归调用来依次确定后面位的字符,从而找到所有可能的字符串。isSub函数是判断该种字符串是否包含子串p,包含返回true且count++;

代码实现:
#include
#include
using namespace std;
bool isSub(string s1,string s2){
string::size_type idx;
idx=s1.find(s2);
if(idxstring::npos)
return false;
else
return true;
}
void adress(string s,string result,long &count,int n,int tag){
if(tag
n){
if(isSub(result,s))
count++;
}
else{
for(char i=‘a’;i<=‘z’;i++){
string r=result;
r.push_back(i);
adress(s,r,count,n,tag+1);
}
}
}
int main(){
int T;
cin>>T;
while (T–){
int n;
int M;
cin>>n>>M;
string s;
cin>>s;
string result;
long count=0;
adress(s,result,count,n,0);
cout<<count%100<<endl;
}
return 0;
}

运行结果:

在这里插入图片描述
题目K 福报
员工绩效评估对于任何公司都是很重要的。在绩效考核中,员工会就最近完成的工作编写工作反馈。反馈会被递给他们的上级,然后上级根据收到的反馈来决定绩效。
Alice负责一家知名公司工程部门的绩效考核系统。该部门遵循树形结构。每位员工都有一个直接上级,最上级是部门总监。让上级评估其直接下属的表现并不是很有效。经过深入研究,Alice想出了一个新的绩效考核系统。主要思路是在现有的公司结构中补充每个员工的技术等级。
新的绩效评估流程如下。员工要准备他们的工作反馈,然后向所有比他技术等级高的上级(直接上级和间接上级)递交工作反馈;上级需要花时间审核所有递交给他的工作反馈。Alice对这个新系统感到非常满意,但她不确定这在实践中是否可行。她想知道每个员工审核下属工作反馈所需的时间。你能帮她吗?

解题思路:
创建一个员工树,每个员工结点包含当自身结点的编号,上级结点的编号,技术等级,审核他的工作需要的时间,和他的孩子结点,然后 BFS每个员工 (注意等级高的员工(A) 也可能是等级低的员工(B)的子节点,在BFS过程中,在算A审核所需时间的时候,不可以加上B审核所需的时间)
在这里插入图片描述
具体解法:
创建一个员工树,每个员工结点包含当自身结点的编号,上级结点的编号,技术等级,审核他的工作需要的时间,和他的孩子结点,然后 BFS每个员工(注意等级高的员工(A) 也可能是等级低的员工(B)的子节点,在BFS过程中,在算A审核所需时间的时候,不可以加上B审核所需的时间),比较偶然的是,员工号从0开始递增,和vector储存方式相同,可以安装顺序去初始化结点,这个员工数不是绝对的树,父节点里只储存孩子结点,没有储存孙子结点。

代码实现:
#include
#include
#include
using std::queue;
using std::vector;
using std::cout;
using std::cin;
using std::endl;

int e = 0; // staffs number

struct Node
{
int mi = 0; // 上级节点编号
int ri = 0; // 等级
int ti = 0; // 审核该结点所需的时间
int itself = 0; // 自身节点的编号
vector child {}; // 孩子节点集合
Node(int m, int r, int t, int i) : mi(m), ri®, ti(t), itself(i){ };
};

class Solution
{
public:
Node Initialize(vector<vector > vec, int num);
int BFS(vector &node_staffs, int n);
};

// 初始化编号为 it_num的节点

Node Solution::Initialize(vector<vector > vec, int it_num)
{
Node node(vec[it_num-1][0], vec[it_num-1][1], vec[it_num-1][2], it_num);
for(int i = 0; i < e; i++)
{
if(vec[i][0] == it_num)
{
node.child.push_back(Node(it_num, vec[i][1], vec[i][2], i+1));
}
}
return node;
}

int Solution::BFS(vector &node_staffs, int n)
{
queue que{};
int time = 0;
que.push(node_staffs[n-1]);
int grade = node_staffs[n-1].ri;
while (!que.empty())
{
Node temp = que.front();
que.pop();
if(temp.ri < grade)
time += temp.ti;
auto childs = temp.child;

    for(int i = 0; i < childs.size(); i++)
    {    
        que.push(node_staffs[childs[i].itself - 1]);
    }
 
}   
return time;

}
int main()
{

int information = 0;
if(!(cin >> e))
{
    std::clog << "input is not suitable " << endl;
    exit(EXIT_SUCCESS);
}

vector<vector<int> > staffs(e);
for(int i = 0; i < e; i++)
{
    staffs[i].resize(3);
}
for(int i = 0; i < e; i++)
{
    for(int j = 0; j < 3; j++)
    {
        cin >> information;
        staffs[i][j] = information;
    }
}

vector<Node> node_staffs {};
Solution solu;

// initialize node
for(int i = 1; i <= e; i++)
{
    auto m = solu.Initialize(staffs, i);
    node_staffs.push_back(m);
}

// BFS node
for(int i = 1; i <= e; i++)
{
    cout << solu.BFS(node_staffs, i) << endl;
}
return 0;

}
运行结果:
在这里插入图片描述

题目L 曲奇工厂
曲奇工厂是一个经典好玩的益智游戏,游戏中你的目标是生产至少C 块曲奇;
游戏的规则十分简单;游戏开始时你有0 块曲奇,每分钟可以手工作出S 块曲奇。你也可以从N 个工厂中选择一些买下来;工厂依次编号为1-N,买下第i 个
工厂需要花费Ai 个曲奇饼。但是工厂会为你带来更多收益,买下第i 个工厂后,
每分钟曲奇产出会增加Bi 块。对于每个工厂,你只能买一次;你只能在整数分钟时购买工厂,并且可以一次买
多个工厂。请问达成目标所用最短时间是多少?
解题思路
贪心策略,运用深搜算法遍历所有情况取最优。
具体解法:
运用DFS遍历所有结果,每遍历完成一次,用calc函数计算所需的时间,并且更新所需的最少时间。
代码实现:
#include
#include
using namespace std;
const int N = 10;
int n, c, s, cnt;
int f[N];
struct {
int x, y;
} a[N], b[N], d[N];
int ans;

int calc(int n) {
int sum = 0, v = s, ans = 0;
for (int i = 1; i <= n; i++) {
if (sum < d[i].x) {
int t = (d[i].x - sum + v - 1) / v;
sum += t * v;
ans += t;
}
sum -= d[i].x;
v += d[i].y;
}
if (sum < c) ans += (c - sum + v - 1) / v;
return ans;
}

void dfs(int now) {
if (now > n) {
for (int i = 1; i <= cnt; i++) f[i] = i;
do {
for (int i = 1; i <= cnt; i++) d[i] = b[f[i]];
ans = min(ans, calc(cnt));
} while (next_permutation(f + 1, f + 1 + cnt));
}
else {
dfs(now + 1);
b[++cnt] = a[now];
dfs(now + 1);
–cnt;
}
}
int main() {
scanf_s("%d%d%d", &n, &c, &s);
for (int i = 1; i <= n; i++)
scanf_s("%d%d", &a[i].x, &a[i].y);
ans = (c + s - 1) / s;
dfs(1);
printf("%d\n", ans);
}
运行结果:
在这里插入图片描述

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值