7-1 自动打包机
一条哈密瓜自动打包流水线的工作程序是这样的:首先系统设定每箱哈密瓜应该有的总重量 W;然后传送带将一只只哈密瓜输送到一个自动称重设备上,根据称重结果进行以下操作:
- 如果称上的总重量正好达到 W,则将称上的所有哈密瓜装箱送走;
- 如果称上的总重量还不到 W,则将这只哈密瓜留在称上;
- 如果称上的总重量超过了 W,则将这只哈密瓜放到一边暂不处理。
本题就请你写个程序统计一下,究竟有多少只哈密瓜被装了多少箱?
输入格式:
输入第一行给出两个正整数 N(≤1000)和 W(≤104),分别为传送带上哈密瓜的数量和每箱的规定重量(克)。随后一行给出 N 个正整数,是传送带上每只哈密瓜的重量,单果重量不超过 2000 克。假设传送带按照输入的顺序传送哈密瓜到称重设备。
输出格式:
在一行中输出成功装箱的箱子数量和被装箱的哈密瓜的数量。数字间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
12 5000
2000 1500 1800 1000 1800 500 1900 1500 2000 1600 2000 2000
输出样例:
2 7
样例说明:
第 1、2、4、6 只瓜正好一箱;第 7、8、10 只瓜正好一箱。
AC代码:
注意:没装成一箱的哈密瓜不能算入被装箱的哈密瓜的数量。使用cntG预算出哈密瓜的数量,等装箱之后将其并入finalCnt中。cntbox计算箱子的数量根据题目要求计算即可。
#include "bits/stdc++.h"
using namespace std;
int main()
{
int N, W;
cin >> N >> W;
int sum = 0;
int cntbox = 0;
int cntG = 0;
int finalCnt = 0;
for(int i = 0; i < N; i++)
{
int num;
cin >> num;
if(sum + num < W){
sum += num;
cntG++;
}else if(sum + num == W){
cntbox++;
finalCnt += (cntG + 1);
cntG = 0;
sum = 0;
}
}
cout << cntbox << " " << finalCnt << endl;
return 0;
}
7-2 用药统计
上图是知乎上的一个问题:“在计算机中,有没有那种把很多病历导入,然后用计算机统计哪种药用的多的语言。?”虽然这个问题有错别字和标点符号错误,但好心的你还是帮他做一个吧 —— 给定 N 位病人病历中的用药纪录,请你统计被用的最多的那种药,并且列出其治疗过的疾病。
输入格式:
输入第一行给出正整数 N(≤104),为病历个数。随后 N 行,每行给出一条病历信息,格式如下:
疾病编号 K 药物编号1 药物编号2 …… 药物编号K
其中疾病编号是一个 4 位数字,题目保证每条病历中的疾病编号都是不同的;K
是不超过 10 的正整数,为该疾病用药的种类数;药物编号是一个以 MD
开头、后面跟 5 位数字的字符串。题目保证在一条病历中没有重复的药物编号。
输出格式:
首先在第一行输出被用的最多的那种药的编号,及其被用的次数,其间以一个空格分隔。如果有并列,则只输出编号最小(即 MD
后那串数字最小)的那个。随后按照输入的顺序输出该药物治疗过的疾病的编号,每行一个。
输入样例:
5
0012 3 MD10031 MD99132 MD42107
1024 2 MD99132 MD34821
2048 2 MD27845 MD10031
0149 2 MD34821 MD55802
0035 3 MD55802 MD99132 MD10031
输出样例:
MD10031 3
0012
2048
0035
AC代码:
定义结构体数组 Virus v[n] 记录各种疾病,在结构体中定义疾病id和可治疗药物的集合sets。
通过map集合的键值对关系记录使用最多的药的编号以及被用次数,在此期间计算出用药最多的次数。之后遍历集合,得到使用最多次的药的名称和次数,打印。
最后遍历结构体数组,凡是在集合中能够查到当前药名maxMed的,打印疾病id,使用%04d保证四位id的输出。
#include "bits/stdc++.h"
using namespace std;
map<string, int> cntmp;
struct Virus{
int id;
set<string> sets;
};
Virus v[10005];
int main()
{
int N;
cin >> N;
int maxValue = 0;
for(int i = 0; i < N ; i++)
{
set<string> st;
int id, K;
cin >> id >> K;
for(int j = 0; j < K; j++)
{
string med;
cin >> med;
if(cntmp.find(med) == cntmp.end()){
cntmp[med] = 1;
}else{
cntmp[med]++;
}
if(cntmp[med] > maxValue){
maxValue = cntmp[med];
}
st.insert(med);
}
v[i].id = id;
v[i].sets = st;
}
string maxMed = "";
for(map<string, int>::iterator it = cntmp.begin(); it != cntmp.end(); it++){
if(it->second == maxValue){
maxMed = it->first;
cout << it->first << " " << it->second << endl;
break;
}
}
for(int i = 0; i < N; i++){
if(v[i].sets.find(maxMed) != v[i].sets.end()){
printf("%04d\n", v[i].id);
}
}
return 0;
}
7-3 五彩斑斓的黑
上面这张图是一条五彩斑斓的黑蛇。我们从头到尾扫描了蛇的身体,将获得的颜色 RGB 值顺序列出。下面就请你统计一下,这条五彩斑斓的黑蛇身上共有多少种不同的颜色,并且将每种不同的颜色按照它们第一次被扫描到的顺序输出。
输入格式:
输入首先在第一行中给出一个正整数 N(≤105),随后一行给出 N 个颜色的 RGB 值,每个颜色的格式为 RRR.GGG.BBB
,其中 RRR
、GGG
、BBB
的值在区间 [0,255] 内。相邻颜色间以 1 个空格分隔。
输出格式:
在第一行中输出不同颜色的数量。第二行按照输入相同的格式、每种颜色第一次被扫描到的顺序输出各种不同颜色的值。相邻颜色间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
7
000.000.000 255.255.255 000.000.000 000.123.072 000.000.000 000.000.000 000.123.072
输出样例:
3
000.000.000 255.255.255 000.123.072
AC代码:
利用set集合(基于红黑树实现高效查找)【补:利用unordered_set底层基于哈希表也很快】的去重效果判断当前元素是否已经存在于集合中,不存在则添加与vector容器中,最后遍历vector容器即可。
#include "bits/stdc++.h"
using namespace std;
vector<string> v;
set<string> st;
int main()
{
int N;
cin >> N;
for(int i = 0; i < N; i++)
{
string color;
cin >> color;
if(st.find(color) == st.end()){
st.insert(color);
v.push_back(color);
}
}
cout << v.size() << endl;
for(int i = 0; i < v.size(); i++)
{
if(i) cout << " ";
cout << v[i];
}
return 0;
}
7-4 假新闻
所有的新闻网站都是有自己立场的,只是有些能比较好地平衡事实与己见。“假新闻”是指为了误导大众对真实事件的理解而做的故意歪曲事实的报导。
为了甄别新闻媒体是否属于假新闻媒体,我们可以注意观察若干不同的新闻网站对同一个重要事件的报导。甄别算法如下:
- 首先选择 N 家新闻网站。
- 对每一个重要的事件,扫描每家网站,用不同的整数表示报导的不同观点。
- 定义网站 i 报导假新闻的可能性为 Fi=ni/N,其中 ni 是与网站 i 持不同观点的其它新闻网站数量。
- 找出报导假新闻可能性最大的一家或几家网站。
- 对每家网站,统计其成为最可能的假新闻网站的次数,最后找出次数最多的那家,就是在最多情况下最可能报导假新闻的网站。
输入格式:
输入第一行给出两个正整数 N (≤ ),为关注的新闻网站的数量;以及 M(≤100),为关注的重要事件的数量。随后 M 行,每行按以下格式给出各个新闻网站报导的观点:
R1 R2 ... RN
其中 Ri 是区间 [−,
] 内的整数,表示网站 i 的观点。
输出格式:
在一行中输出在最多情况下最可能报导假新闻的网站的编号。题目保证解是唯一的。
输入样例:
4 6
4 2 7 7
1 1 1 3
2 9 9 5
-1 -1 -1 -1
-2 2 -2 2
1 1 3 4
输出样例:
4
样例说明:
每次中大事件对应的 Fi 的值如下:
事件 1: 3/4 3/4 2/4 2/4 --> 1 和 2 疑似
事件 2: 1/4 1/4 1/4 3/4 --> 4 疑似
事件 3: 3/4 2/4 2/4 3/4 --> 1 和 4 疑似
事件 4: 0 0 0 0 --> 全都疑似
事件 5: 2/4 2/4 2/4 2/4 --> 全都疑似
事件 6: 2/4 2/4 3/4 3/4 --> 3 和 4 疑似
所以只有 4 号网站有 5 次都被列为最大疑似,其它都只有 3 或 4 次。
AC代码:
使用unordered_map记录每一次重要事件的意见与次数。已知题目可能性为 Fi=ni/N,此处的 “/N”都存在所以只需要比较分子部分ni就可以了。这里注意ni的概念: ni 是与网站 i 持不同观点的其它新闻网站数量。比如样例 “4 2 7 7”,第一个4是网站一的观点,剩下三个和网站一的观点都不相同所以n1 = 3(这个地方搞不清楚可能会出现测试点3答案错误)。在代码中使用posF表示ni,使用maxPos计算出最大的ni。
现在得到了最大的ni即maxPos了(也就是与大多数网站意见相差最大的量--->即最有可能造假的情况),重复上述计算posF也就是ni,然后当计算的ni的值为maxPos的时候即表示本次该网站疑似假新闻,对应map中的值+1。
最后使用迭代器遍历map,就能算出那唯一的(已知),最可能报导假新闻的网站的编号。
#include "bits/stdc++.h"
using namespace std;
map<int, int> mp;
int main()
{
int N, M;
cin >> N >> M;
for(int i = 0; i < M; i++)
{
unordered_map<int, int> cntmp;
vector<int> v;
for(int j = 0; j < N; j++)
{
int R;
cin >> R;
v.push_back(R);
if(cntmp.find(R) == cntmp.end()){
cntmp[R] = 1;
}else{
cntmp[R]++;
}
}
int maxPos = 0;
for(int j = 0; j < N; j++)
{
int posF = N - cntmp[v[j]];
if(posF > maxPos){
maxPos = posF;
}
}
for(int j = 0; j < N; j++)
{
int posF = N - cntmp[v[j]];
if(posF == maxPos){
if(mp.find(j + 1) == mp.end()){
mp[j + 1] = 1;
}else{
mp[j + 1]++;
}
}
}
}
int maxFack = 0;
int Fackable = 0;
for(map<int, int>::iterator it = mp.begin(); it != mp.end(); it++){
if(it->second > maxFack){
maxFack = it->second;
Fackable = it->first;
}
}
cout << Fackable << endl;
return 0;
}
7-5 静态链表的秩
静态链表是指将一个有 n 个结点的链表存放在一个有 n 个单元的数组里,每个数组单元包含一个整型的 data
域和一个 next
指针,指针里存的是链表下一个结点在数组里对应的单元下标。题目保证给出的链表是一个线性表,即除了第一个结点外,每个结点有其唯一的前驱结点;除了最后一个结点外,每个结点有其唯一的后继结点。
你的任务是将这个链表上的结点顺序编号,即从第一个结点开始,从 1 到 n 给每个结点顺次编号 —— 这个编号就叫结点的“秩”。
输入格式:
输入第一行给出一个正整数 n (≤ ),为链表中的结点个数。第二行给出 n 个数字,第 i (i=0,⋯,n−1) 个数字对应数组第 i 个单元存储的 next(i) 的值,空指针用 −1 表示。数字间以空格分隔。
输出格式:
在一行中输出 n 个数字,第 i (i=0,⋯,n−1) 个数字对应第 i 个单元存储的结点在链表中的秩。数字间以 1 个空格分开,行首尾不得有多余空格。
输入样例:
5
3 -1 4 1 0
输出样例:
3 5 1 4 2
样例说明:
输入的链表为 2->4->0->3->1->空。于是第 0 个单元所存的结点的秩是 3,因为它是链表中的第 3 个结点;第 1 个单元所存的结点的秩是 5,因为它是链表中的第 5 个结点;以此类推。
反面教材:
先说一个结论(教训):如果想通过遍历找链表头节点,用cnt记录链表节点个数,当cnt == n时找到了编号这种思路:
如果你从前向后正着找:
如果你从后向前倒着找:
所以这道题,一定要用好:正难则反!!!
所谓正难则反,链表头节点找起来费时间,每找一个节点都得从头遍历到尾。那我找尾结点也就是next(i) = -1的点这不就完事儿了!!!
AC代码:
初始化cnt为n,使用map集合键值对逆向记录<next, pre>,对数组v[n]进行编号最后遍历打印即可,使用样例举个例子:
初始化cnt = n = 5,
- 当找到-1后,mp[-1] = 1 为当前 -1 所在的索引位置,v[mp[-1]] = v[1] = cnt = 5,cnt--;
- 拿着 1 这个索引继续,mp[1] = 3,v[mp[1]] = v[3] = cnt = 4,cnt--;
- 找到 3 这个索引继续,mp[3] = 0,v[mp[3]] = v[0] = cnt = 3,cnt--;
- 找到 0 这个索引继续,mp[0] = 4,v[mp[0]] = v[4] = cnt = 2,cnt--;
- 找到 4 这个索引继续,mp[4] = 2,v[mp[4]] = v[2] = cnt = 1,cnt--;
- cnt == 0,退出while循环。
从0~n打印v[n],即 3 5 1 4 2 为样例输出。
#include "bits/stdc++.h"
using namespace std;
map<int, int> mp;
int main()
{
int n;
cin >> n;
int res[n];
for(int i = 0; i < n; i++)
{
int next;
cin >> next;
mp[next] = i;
}
map<int, int>::iterator it = mp.begin();
while(it->first != -1){
it++;
}
int cnt = n;
int num = it->second;
while(cnt > 0)
{
res[num] = cnt;
cnt--;
num = mp[num];
}
for(int i = 0; i < n; i++)
{
if(i) cout << " ";
cout << res[i];
}
return 0;
}