1. 核桃的数量(水题)
思路:求两个数的最大公约数可以用欧几里得算法(辗转相除法),即 a与b的最大公约数 等于 b与(a对b取余) 的最大公约数,当b等于0时,a与b的最大公约数是a。两个数的最小公倍数 等于 其乘积除以两个数的最大公约数。
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
int main()
{
int a, b, c;
cin >> a >> b >> c;
cout << lcm(lcm(a, b), c) << endl;
return 0;
}
2. 打印十字图(找规律)
思路:n为内十字外面的圈的数量
容易看出这是一个中心对称图形,所以只需得到1/4图像然后对称输出即可。
对于这1/4图像,可以定义一个(3+n*2)*(3+n*2)的数组,先设置将左上角的4个.(点)和最外层的一圈$,然后将这一圈$不断向右下移动一格并变符号即可(一圈$一圈.一圈$一圈.)。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<vector<char> >data(3*n+2, vector<char>(3*n+2, ' '));
data[0][0] = data[0][1] = data[1][0] = data[1][1] = '.';
data[1][2] = data[2][1] = data[2][2] = '$';
for (int i = 2; i < 3 + n * 2; ++i)
{
data[0][i] = data[i][0] = '$';
}
for (int i = 1; i < 3 + n * 2; ++i)
{
for (int j = 1; j < 3 + n * 2; ++j)
{
if (data[i][j] == ' ')
{
if (data[i - 1][j - 1] == '$')
data[i][j] = '.';
else
data[i][j] = '$';
}
}
}
for (int i = 0; i < 3 + n * 2; ++i)
{
for (int j = 0; j < 3 + n * 2; ++j)
{
cout << data[i][j];
}
for (int j = 1 + n * 2; j >= 0; --j)
{
cout << data[i][j];
}
cout << endl;
}
for (int i = 1 + n * 2; i >= 0; --i)
{
for (int j = 0; j < 3 + n * 2; ++j)
{
cout << data[i][j];
}
for (int j = 1 + n * 2; j >= 0; --j)
{
cout << data[i][j];
}
cout << endl;
}
return 0;
}
3. 带分数(搜索)
思路:对问题描述进行分析,可以得出4个条件:
1)num == a + b / c
2) a < num
3) b >= c
4) b % c == 0
所以,可以使用DFS(深度优先搜索)得到数字1-9的全排列,然后对该集合中的每个项根据上面的4个条件进行剪枝,得出结果并输出。提交之后发现运行超时。。。。。。
#include <bits/stdc++.h>
using namespace std;
int num, tCount;
//对1-9的全排列剪枝
void filter(int num, string str)
{
int a, b, c;
//求得num的位数
char tNum[7];
itoa(num, tNum, 10);
int numLen = strlen(tNum);
//a < num
for (int i = 1; i <= numLen; ++i)
{
a = atoi(str.substr(0, i).c_str());
if (i == numLen && num <= a)
continue;
for (int j = ceil((9 - i) / 2); j <= 8 - i; ++j)
{
b = atoi(str.substr(i, j).c_str());
c = atoi(str.substr(i + j, 9 - i - j).c_str());
if (b < c)
continue;
if (b % c != 0)
continue;
if (num != a + b / c)
continue;
++tCount;
}
}
}
//生成1-9的全排列
bool flag[10] = {false};
void dfs(bool* tflag = flag, int index = 1, string str = "")
{
if (index == 10)
filter(num, str);
for (int i = 1; i < 10; ++i)
{
if (flag[i])
continue;
flag[i] = true;
char t = '0' + i;
str = str + t;
dfs(tflag, index + 1, str);
str = str.substr(0, (int)str.size() - 1);
flag[i] = false;
}
}
int main()
{
cin >> num;
dfs();
cout << tCount << endl;
return 0;
}
然后,参考了其他文章的代码稍整理后顺利AC,原文章链接:https://blog.csdn.net/fanke666/article/details/69644216
大体的思路为:由于abc一共9位数字,a至少1位,而b>=c,所以c至多有4为,所有设置两个for循环,外层从1到num-1遍历a,内层从1到10000-1遍历c,而b由num = a + b / c得到。至于前一种方法运行超时的原因,我分析可能的原因是先生成了全排列之后再进行分割判断,而没有在全排列的前几位已经不满足条件时终止生成,欢迎大家在评论中给出建议,哈哈哈哈哈哈。
#include <bits/stdc++.h>
using namespace std;
int flag[10];
//统计1-9的数目并检测是否含0
int statistic(int x) {
do {
int tmp = x % 10;
if(tmp == 0)
{
return 0;
}
flag[tmp]++;
} while(x = x / 10);
return 1;
}
//检测当前时候有重复的1-9
int statisticOk() {
for(int i = 1; i < 10; i++)
{
if(flag[i] != 1)
{
return 0;
}
}
return 1;
}
int main()
{
int num, count = 0, a, b, c;
cin >> num;
//从1到num-1遍历a
for(a = 1; a < num; a++)
{
//每次对flag清0
memset(flag, 0, sizeof(flag));
if(!statistic(a))
{
continue;
}
//从1到10000-1遍历c,由num = a + b / c得到b
//暂时保存a的数字统计,以便于回退再测试下一个c或b
int tFlag[10];
for(c = 1; c < 10000; c++)
{
memcpy(tFlag, flag, sizeof(flag));
if(!statistic(c))
{
memcpy(flag, tFlag, sizeof(flag));
continue;
}
b = (num - a) * c;
if(!statistic(b))
{
memcpy(flag, tFlag, sizeof(flag));
continue;
}
if(!statisticOk())
{
memcpy(flag, tFlag, sizeof(flag));
continue;
}
memcpy(flag, tFlag, sizeof(flag));
count++;
}
}
cout << count << endl;
return 0;
}
4. 剪格子(搜索、DFS)
思路:图的深度优先遍历算法的应用,较简单
#include <bits/stdc++.h>
using namespace std;
int m, n, sum = 0;
int data[10][10];
bool flag[10][10];
int dx[4] = {0,1,0,-1};
int dy[4] = {1,0,-1,0};
int dfs(int x, int y, int num)
{
if(num == sum / 2)
return 1;
for(int i = 0;i < 4; ++i)
{
int nx = x + dx[i];
int ny = y + dy[i];
if(nx < 1 || ny <1 || nx > n || ny > m || flag[nx][ny] || num + data[nx][ny] > sum / 2)
continue;
flag[nx][ny] = true;
int res = dfs(nx, ny, num + data[nx][ny]);
if(res)
return res + 1;
flag[nx][ny] = false;
}
return 0;
}
int main()
{
memset(flag, 0, sizeof(flag));
cin >> m >> n;
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
cin >> data[i][j];
sum += data[i][j];
}
}
//和为奇数肯定不符合要求
if(sum % 2 == 1)
{
cout<<0<<endl;
}
else
{
flag[1][1] = true;
cout << dfs(1, 1, data[1][1]) <<endl;
}
return 0;
}
5. 错误票据(水题)
思路:对输入的数据进行排序后,遍历检查第2到n-1个元素的值,若与前一个元素相等,则为重号ID,因为可能与值最大的ID重号,所以应该检查2到n个元素;断号ID检查第2到n-1个元素是否与前一个元素的差为2即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int key;
vector<int>data;
cin >> key;
while(cin >> key)
data.push_back(key);
sort(data.begin(), data.end());
int a, b;
for (int i = 1; i < (int)data.size(); ++i)
{
if (data[i] == data[i - 1] + 2)
a = data[i] - 1;
if (data[i] == data[i - 1])
b = data[i];
}
cout << a << " " << b << endl;
return 0;
}
6. 翻硬币(贪心)
思路:比较源字符串和目标字符串,找出第一个不同的字符,将这个字符与其后面的字符进行o<-->*翻转;若该字符是串尾,则翻转其与之前的字符,但实际上这种情况是不存在的,因为检查到串尾元素不同,也就意味着之前的其他字符均相同,而每次要翻转相邻的两个字符,所以题目将无法得到解。至于为什么这样翻次数最少我还没用理解,欢迎大家在评论区讨论分享。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string a, b;
cin >> a >> b;
int len = (int)a.size();
int i, cnt = 0;
for (i = 0;i < len; ++i)
{
if (a[i] != b[i])
{
++cnt;
if (a[i] == '*')
a[i] = 'o';
else
a[i] = '*';
if (i == len - 1)
{
if (a[i - 1] == '*')
a[i - 1] = 'o';
else
a[i - 1] = '*';
}
else
{
if (a[i + 1] == '*')
a[i + 1] = 'o';
else
a[i + 1] = '*';
}
}
}
cout << cnt << endl;
return 0;
}
7. 连号区间数(水题)
思路:若[L, R]为连号区间,则其中的最大数max减最小数min==区间内元素数目减1==R减L。所以遍历每个子串并且判断是否符合条件即可,时间复杂度为O(n^2)。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int num, cnt = 0;
cin >> num;
vector<int>data(num);
for (int i = 0; i < num; ++i)
{
cin >> data[i];
}
int mmax, mmin;
for (int i = 0;i < num; ++i)
{
mmax = mmin = data[i];
for (int j = i; j < num; ++j)
{
if (data[j] > mmax)
mmax = data[j];
if (data[j] < mmin)
mmin = data[j];
if (mmax - mmin == j - i)
++cnt;
}
}
cout << cnt << endl;
return 0;
}
8. 买不到的数目(数论)
思路:数论的知识,若自然数a,b互质,则不能表示成ax+by(x, y为非负整数)的最大整数是ab-a-b。当然,题目中没有提到输入的两个数互质,但若a, b不互质,那么不存在最大值,例如2,4,所有4n-1和4n-3(n>0且n为整数)都不能得到组合。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
cout << a * b - a - b << endl;
return 0;
}
9. 大臣的旅费(Floyd算法/DFS)
方法一:Floyd算法(时间复杂度O(n^3),因运行超时只能得到75分)
思路:首先通过题目描述可以得到,由于节省开支,任意两个城市之间连通且路径唯一,即任意两个城市间的最短路径和最长路径是一条路径。通过Floyd算法求出任意两点间的最短路径,然后找出其中最大的数x,按照题目中的计费规则,费用为10*x+x!。
注意:本人第一次使用Floyd算法进行问题求解,编程过程中遇到的问题在下面一一列出
1)若路径长度的存储数据类型为int,那么容易想到用一个很大的数表示两点之间不可达,但如果使用INT_MAX(系统定义的int类型最大值)就会出现一个问题,即INT_MAX+INT_MAX会产生数据溢出,结果为负数。举个例子,data[a][b]为1,data[a][c]和data[b][c]都为INT_MAX, 即data[a][b]会被更新为INT_MAX+INT_MAX数据溢出后的负数。针对这个问题可以找一个相对较大的数代替例如1000000,或者进行+等操作前判断是否存在INT_MAX等。
2)不要为了图省事将data[i][i]也置为1000000(自定义的极大值,后面以dmax表达)并且搜索最大数时不排除data[i][i],例如i = j = 2,k = 3,data[i][j] = dmax,data[i][k] = 10,data[j][k] = 10,那么有data[i][j] > data[i][k] + data[k][j],即data[i][j]将被更新为20,而这个20可能会被作为最大数搜索到并输出,佛。。。。。。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int num;
cin >> num;
vector<vector<int> >data(num + 1, vector<int>(num + 1, 1000000));
for (int i = 1; i <= num; ++i)
data[i][i] = 0;
int a, b, dis;
while(cin >> a >> b >> dis)
data[a][b] = data[b][a] = dis;
//floyd算法
for (int k = 1; k <= num; ++k)//中转节点
{
for (int i = 1; i <= num; ++i)
{
for (int j = 1; j <= num; ++j)
{
if (data[i][j] > data[i][k] + data[k][j])
data[i][j] = data[j][i] = data[i][k] + data[k][j];
//或写为
//data[i][j] = data[j][i] = min(data[i][j], data[i][k] + data[k][j]);
}
}
}
//找出两点之间的最大路径长度
int mmax = 0;
for (int i = 1; i <= num; ++i)
{
for (int j = 1; j <= num; ++j)
{
if (data[i][j] > mmax)
mmax = data[i][j];
}
}
//输出结果
int sum = 10 * mmax;
for (int i = 1;i <= mmax; ++i)
sum += i;
cout << sum;
return 0;
}
该方法可以拿到满分。
思路:由题目描述可知,n个城市共n-1条道路,各个城市到首都(1号结点)有且仅有一条道路。所以,可以想到,最长路径path 会包含 从首都结点1到离它最远的结点的一部分。
下面简单证明一下:
假设由1开始dfs的最远结点为i,且path不包含1到i的一部分,那么可能要两种情况:
情况1:path为1-j且1-k的路径之和。
但1-i>1-j且1-i>1-k,所以path应该为1-i与1-j或者1-i与1-k之和,故情况1不成立。
情况2:path不经过1结点。
1-i>1-k,故m-i>m-k,故情况2也不成立。
所以最长路径path肯定以i为端点,故求出i后再以i为源点DFS出最长的路径path即可。
#include <bits/stdc++.h>
using namespace std;
struct node
{
int to;
int cost;
};
//图结点
vector<node> v[1000000];
//标记图结点是否被访问,全局变量默认为false
bool flag[1000000];
//tar保存终点下标, mmax为最长路径距离
int tar, mmax = -1;
void dfs(int s, int sum)
{
flag[s] = true;
if(sum > mmax)
mmax = sum, tar = s;
for(int i = 0; i < (int)v[s].size(); ++i)
{
node t = v[s][i];
if(flag[t.to] == false)
dfs(t.to, sum + t.cost);
}
}
int main()
{
int num;
cin >> num;
int a, b, dis;
for(int i = 0; i < num - 1; ++i)
{
cin >> a >> b >> dis;
v[a].push_back(node{b, dis});
v[b].push_back(node{a, dis});
}
dfs(1, 0);
fill(flag, flag + 1000000, false);
dfs(tar, 0);
//输出结果
cout << mmax * 10 + (1 + mmax) * mmax / 2;
return 0;
}