此博客原文地址:https://www.cnblogs.com/BobHuang/p/12312129.html
1.6172: Alice视察
本题目比较困难,我们将信息传递出去,而且要满足最短的,那么其实只需要n-1条边,是一棵无向树。在数据结构中有“最小生成树”,有两个算法,分别是Kruskal(加边)及Prime(加点)。这个题目数据量比较小,不卡算法,你可以实现任一算法AC。题解所用的是Kruskal。
这个算法的思想可以这样理解,我将所有的边按照权值进行排序,我必定会先选择最小的边,怎么确定这个边可以用另一数据结构“并查集”,他也是一棵无向树。没有连接这条边肯定要选,所以也有点贪心的意思。最后n-1条边将n个点连接起来就是最小的。
用C++11进行提交,否则CE
#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 1005;
//定义并查集父亲数组
int fa[N];
//并查集查找祖先函数
int Find(int x)
{
//相等,已经找到
if (x == fa[x])
return x;
//没有,压缩一下
return fa[x] = Find(fa[x]);
}
//并查集联合函数
void Union(int x, int y)
{
//寻找x的祖先
x = Find(x);
//寻找y的祖先
y = Find(y);
//不一个进行合并
if (x != y)
fa[x] = y;
}
//边数组,和用struct数组一样
pair<int, pair<int, int>> V[M];
#define fi first
#define se second
int main()
{
int n, m;
cin >> n >> m;
//并查集父亲数组初始化
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 0, x, y, z; i < m; i++)
{
cin >> x >> y >> z;
V[i] = {z, {x, y}};
}
//pair运算符不需要重载
sort(V, V + m);
int ans = 0;
for (int i = 0, x, y, z; i < m; i++)
{
//两个点不相连,加入
if (Find(V[i].se.fi) != Find(V[i].se.se))
{
ans += V[i].fi;
Union(V[i].se.fi, V[i].se.se);
}
}
cout << ans << "
";
}
2.6178: Alice运快递
第一问是一个很经典的01背包问题,一个物品可以选一次,所以我们这时候的状态是从前一个状态过来的。当前物品是否可以放下,是不是更大都是需要我们考虑的。01背包只能选一次,我们可以优化只用一维dp数组,第二重循环需要倒着进行。
第二问是个很简单的贪心问题,我们可以选择体积最小的货物装上。
#include <bits/stdc++.h>
using namespace std;
int w[1005], c[1005], dp[100005];
int main()
{
int m, n;
while (cin >> m >> n)
{
for (int i = 0; i < n; i++)
cin >> w[i] >> c[i];
//求最大,dp数组清空
memset(dp, 0, sizeof(dp));
//循环访问物品
for (int i = 0; i < n; i++)
{
//对dp[i-1][j]查询,看看还能不能放下
//少一维循环,必须从大到小保证只放一次
for (int j = m; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
}
sort(w, w + n);
int ans = 0, sum = 0;
for (int i = 0; i < n; i++)
{
//贪心只取最小
if (sum + w[i] <= m)
sum += w[i], ans++;
else
break;
}
cout << dp[m] << " " << ans << "
";
}
return 0;
}
3.6173: 相同行程查询
这个题目有些困难,我们需要知道这个人的车次,之后我们会找到人然后对应到这个车次。最终我们要查询的是车次,当然我们可以进行排序之后二分。不过这个题目有个更好用的东西,叫map,是stl中的一个。map<key,value>也叫键值对,前面可以把放键,可以理解为就是一个下标,后面放值。所以就是人和车次的map以及车次和人数的map。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
//m为人和车次的键值对
map<string, string> m;
//p为车次和人数的键值对
map<string, int> p;
int main()
{
int n, a;
string x, y;
cin >> n;
while (n--)
{
//读入车次和人数
cin >> x >> a;
while (a--)
{
//读入人名
cin >> y;
//建立人名和车次的键值对
m[y] = x;
}
}
cin >> n;
while (n--)
{
//读入生病的人
cin >> y;
//对生病的车次进行+1操作
p[m[y]]++;
}
cin >> n;
while (n--)
{
cin >> x;
//直接输出车次上生病的人数
cout << p[x] << "
";
}
return 0;
}
4.6177: Alice玩汉诺
这个题目我们可以采用常规做法,就是我们递归的时候可以统计移动次数。
当然也可以找到递推式,数量级大的情况就必须使用递推式了。
A->B=(上一次)A->C->B
B->C=(上一次)B->A->C
C->A=(上一次)C->B->A
我这里用了递归写法,我们可以设置变量去统计他们,在每次移动的时候去统计。
#include <bits/stdc++.h>
using namespace std;
int ans[8];
void move(char a, char b)
{
//进行判断归到不同变量
if (a == 'A' && b == 'B')
ans[0]++;
if (a == 'A' && b == 'C')
ans[1]++;
if (a == 'B' && b == 'A')
ans[2]++;
if (a == 'B' && b == 'C')
ans[3]++;
if (a == 'C' && b == 'A')
ans[4]++;
if (a == 'C' && b == 'B')
ans[5]++;
}
void hanoi(int n, char a, char b, char c)
{
if (n == 0)
return;
hanoi(n - 1, a, c, b);
//必然把a移动到c
move(a, c);
hanoi(n - 1, b, a, c);
}
int main()
{
int n;
while (cin >> n)
{
//清空ans数组
memset(ans, 0, sizeof(ans));
hanoi(n, 'A', 'B', 'C');
//一次输出答案
cout << "A->B: " << ans[0] << "
";
cout << "A->C: " << ans[1] << "
";
cout << "B->A: " << ans[2] << "
";
cout << "B->C: " << ans[3] << "
";
cout << "C->A: " << ans[4] << "
";
cout << "C->B: " << ans[5] << "
";
}
return 0;
}
5.6179: Alice排数字
其实我们可以给其中的8和2拿出来看看有多少个282,排列一定是282828282····
也就是28不断循环的,其中282的个数和2和8均有关,n个2就有n-1个282,m个8就有m个282,两者取小,当然不能是负数。
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
while (cin >> s)
{
int a[10] = {0};
for (int i = 0; s[i]; i++)
{
//'0'是48,数字下标所对位置++
a[s[i] - '0']++;
}
//2的个数和8的个数取小,有可能编程负数
cout << max(0, min(a[2] - 1, a[8])) << "
";
cout << "Happy Birthday!
";
}
return 0;
}
6.6181: Alice与闪电
这个一个比较复杂的循环题,我们可以将其分两三输出,前m/2行,中间行,后m/2行,前m/2行前面的空格分析下,为m-i个,*为i+1个,中间行全是是n个,然后依次类推。至于中间用空行隔开,我们可以用一个旗子来表示,刚开始没有插旗子,不能输出空行,执行一次就插上了旗子,当然是否要输出空行要在插旗子之前。详见代码flag的操作。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
//定义旗子
int flag = 0;
while (cin >> n)
{
//插了旗子就要进行换行,第一次不会换行
if (flag)cout << "
";
//插旗子
flag = 1;
//找到对应的行
int m = n / 2;
for (int i = 0; i < m; i++)
{
//输出m-i个空格
for (int j = m; j > i; j--)
cout << " ";
//输出i+1个星号
for (int j = 0; j <= i; j++)
cout << "*";
cout << "
";
}
//输出第m行
for (int i = 0; i < n; i++)
{
cout << "*";
}
cout << "
";
//倒着输出每一行
for (int i = m - 1; i >= 0; i--)
{
//输出m-1个空格
for (int j = 0; j < m; j++)
cout << " ";
//输出i+1个星号
for (int j = 0; j <= i; j++)
cout << "*";
cout << "
";
}
}
return 0;
}
7.6180: Alice玩井棋
这是一个比较有趣的游戏,但是不同的思路实现起来难度可能不同,在这里推荐了一个比较简单的实现,
1.横或竖坐标均相同
2.在左上到右下的对角线上
3.在右上到左下对角线上
#include <bits/stdc++.h>
using namespace std;
int main()
{
int x1, x2, x3, y1, y2, y3;
while (cin >> x1 >> y1)
{
cin >> x2 >> y2;
cin >> x3 >> y3;
int f = 0;
//成行或成列
if ((x1 == x2 && x2 == x3) || (y1 == y2 && y2 == y3))
f = 1;
//从左上到右下
if (x1 == y1 && x2 == y2 && x3 == y3)
f = 1;
//从左下到右上
if (x1 + y1 == 4 && x2 + y2 == 4 && x3 + y3 == 4)
f = 1;
if (f)
cout << "Win" << endl;
else
cout << "Continue" << endl;
}
return 0;
}
8.6174: Alice与甜甜圈
空圆柱体的体积,体积为底面积高,
圆环面积为(R*R-r*r)PI
注意这个题目给我们的数是实数
#include<bits/stdc++.h>
using namespace std;
int main()
{
//实数包括整数和小数,是double类型的
double R,r,h;
double PI = acos(-1);//3.1415926536
while(cin>>R>>r>>h)
{
//底面积乘高
double ans = PI*h*(R*R - r*r);
printf("%.2f
",ans);
}
return 0;
}
9.6175: Alice买口罩
10个口罩一定会浪费掉,所以你买到y个口罩,买x个一次,次数为y/x
#include <bits/stdc++.h>
using namespace std;
int main()
{
int x,y;
cin>>x>>y;
//输出次数
cout<<y/x;
return 0;
}
10.6176: 武汉加油!中国加油!
简单C++输出,可以复制粘贴,尽量减少错误
#include <bits/stdc++.h>
using namespace std;
int main()
{
//记得复制粘贴,不要交错语言
cout<<"Fighting, Wuhan! Stay strong, China!
";
}