7-9 点赞狂魔 (25分)
微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明显的特性。本题就要求你写个程序,通过统计每个人点赞的不同标签的数量,找出前3名点赞狂魔。
输入格式:
输入在第一行给出一个正整数N(≤100),是待统计的用户数。随后N行,每行列出一位用户的点赞标签。格式为“Name K F1⋯FK ”,其中Name是不超过8个英文小写字母的非空用户名,1≤K≤1000,Fi (i=1,⋯,K)是特性标签的编号,我们将所有特性标签从 1 到 10^7编号。数字间以空格分隔。
输出格式:
统计每个人点赞的不同标签的数量,找出数量最大的前3名,在一行中顺序输出他们的用户名,其间以1个空格分隔,且行末不得有多余空格。如果有并列,则输出标签出现次数平均值最小的那个,题目保证这样的用户没有并列。若不足3人,则用-补齐缺失,例如mike jenny -
就表示只有2人。
输入样例:
5
bob 11 101 102 103 104 105 106 107 108 108 107 107
peter 8 1 2 3 4 3 2 5 1
chris 12 1 2 3 4 5 6 7 8 9 1 2 3
john 10 8 7 6 5 4 3 2 1 7 5
jack 9 6 7 8 9 10 11 12 13 14
输出样例:
jack chris john
答案:
法一:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e7 + 10;
using namespace std;
struct Node
{
char name[11];
ll f[1111];
int sum;
int times;
} p[111];
int dp[N];
bool cmp(Node x,Node y)
{
if(x.sum>y.sum)
return true;
if(x.sum==y.sum&&x.sum*1.0/x.times>y.sum*1.0/y.times)
return true;
return false;
}
int main()
{
int n;
cin>>n;
map<int,int>dp;
int i;
for(i=1;i<=n;i++)
{
dp.clear();
cin>>p[i].name>>p[i].times;
int j;
int cnt=0;
for(j=1;j<=p[i].times;j++)
{
int k;
cin>>k;
if(!dp[k])
{
cnt++;
dp[k]=1;
}
}
p[i].sum=cnt;
}
sort(p+1,p+1+n,cmp);
if(n>=3)
{
for(i=1; i<=3; i++)
{
if(i==3)
printf("%s\n",p[i].name);
else
printf("%s ",p[i].name);
}
}
else
{
for(i=1;i<=n;i++)
printf("%s ",p[i].name);
for(i=1;i<=3-n;i++)
{
if(i==3-n)
printf("-\n");
else
printf("- ");
}
}
return 0;
}
法二:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e7 + 10;
using namespace std;
struct node
{
string s;
int sum;
double ave;
node(string s1,int sum1,double ave1):s(s1),sum(sum1),ave(ave1) {}
};
bool cmp(node x,node y)
{
if(x.sum==y.sum)
return x.ave<y.ave;
return x.sum>y.sum;
}
int main()
{
vector<node>dp;
set<int>s[111];
int i;
string name;
int n;
cin>>n;
for(i=0; i<n; i++)
{
int y;
cin>>name>>y;
int j;
for(j=0; j<y; j++)
{
int k;
cin>>k;
s[i].insert(k);
}
dp.push_back(node(name,s[i].size(),(double)y/(double)s[i].size()));
}
sort(dp.begin(),dp.end(),cmp);
for(i=0; i<3; i++)
{
if(i!=0)
cout<<" ";
if(i<dp.size())
cout<<dp[i].s;
else
cout<<'-';
}
return 0;
}
7-10 月饼 (25分)
月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。
注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。
输入格式:
每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。
输出格式:
对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。
输入样例:
3 20
18 15 10
75 72 45
输出样例:
94.50
答案:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e7 + 10;
using namespace std;
struct node
{
double w;
double p;
double ave;
} dp[1111];
bool cmp(node x,node y)
{
return x.ave>y.ave;
}
int main()
{
int n;
int v;
cin>>n>>v;
int i;
for(i=1; i<=n; i++)
{
cin>>dp[i].w;
}
for(i=1; i<=n; i++)
{
cin>>dp[i].p;
dp[i].ave=dp[i].p*1.0/dp[i].w;
}
sort(dp+1,dp+1+n,cmp);
double ans=0;
for(i=1;; i++)
{
if(i>n)
break;
if(v<=0)
break;
if(dp[i].w<=v)
{
ans+=dp[i].p*1.0;
v-=dp[i].w;
}
else
{
ans+=v*dp[i].ave;
v=0;
}
}
printf("%.2f\n",ans);
return 0;
}
7-11 名人堂与代金券 (25分)
对于在中国大学MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,总评成绩必须达到 60 分及以上,并且有另加福利:总评分在 [G, 100] 区间内者,可以得到 50 元 PAT 代金券;在 [60, G) 区间内者,可以得到 20 元PAT代金券。全国考点通用,一年有效。同时任课老师还会把总评成绩前 K 名的学生列入课程“名人堂”。本题就请你编写程序,帮助老师列出名人堂的学生,并统计一共发出了面值多少元的 PAT 代金券。
输入格式:
输入在第一行给出 3 个整数,分别是 N(不超过 10 000 的正整数,为学生总数)、G(在 (60,100) 区间内的整数,为题面中描述的代金券等级分界线)、K(不超过 100 且不超过 N 的正整数,为进入名人堂的最低名次)。接下来 N 行,每行给出一位学生的账号(长度不超过15位、不带空格的字符串)和总评成绩(区间 [0, 100] 内的整数),其间以空格分隔。题目保证没有重复的账号。
输出格式:
首先在一行中输出发出的 PAT 代金券的总面值。然后按总评成绩非升序输出进入名人堂的学生的名次、账号和成绩,其间以 1 个空格分隔。需要注意的是:成绩相同的学生享有并列的排名,排名并列时,按账号的字母序升序输出。
输入样例:
10 80 5
cy@zju.edu.cn 78
cy@pat-edu.com 87
1001@qq.com 65
uh-oh@163.com 96
test@126.com 39
anyone@qq.com 87
zoe@mit.edu 80
jack@ucla.edu 88
bob@cmu.edu 80
ken@163.com 70
输出样例:
360
1 uh-oh@163.com 96
2 jack@ucla.edu 88
3 anyone@qq.com 87
3 cy@pat-edu.com 87
5 bob@cmu.edu 80
5 zoe@mit.edu 80
答案:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e7 + 10;
using namespace std;
struct node
{
int r;
string s;
int p;
} dp[11111];
bool cmp(node x,node y)
{
if(x.p==y.p)
return x.s<y.s;
return x.p>y.p;
}
int main()
{
int n,g,k;
int i;
int ans=0;
cin>>n>>g>>k;
for(i=0; i<n; i++)
{
cin>>dp[i].s>>dp[i].p;
if(dp[i].p>=g)
{
ans +=50;
}
else if(dp[i].p>=60&&dp[i].p<g)
{
ans +=20;
}
}
cout<<ans<<endl;
sort(dp,dp+n,cmp);
int cnt=1;
dp[0].r = 1;
cnt=1;
for(i=1; i<n; i++)
{
cnt++;
if(dp[i].p == dp[i-1].p)
{
dp[i].r = dp[i-1].r;
}
else
{
dp[i].r = cnt;
}
}
for(i=0; i<n; i++)
{
if(dp[i].r<=k)
{
cout<<dp[i].r<<" "<<dp[i].s<<" "<<dp[i].p<<endl;
}
}
return 0;
}
7-12 单身狗 (25分)
“单身狗”是中文对于单身人士的一种爱称。本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱。
输入格式:
输入第一行给出一个正整数 N(≤50000),是已知夫妻/伴侣的对数;随后 N 行,每行给出一对夫妻/伴侣——为方便起见,每人对应一个 ID 号,为 5 位数字(从 00000 到 99999),ID 间以空格分隔;之后给出一个正整数 M(≤10000),为参加派对的总人数;随后一行给出这 M 位客人的 ID,以空格分隔。题目保证无人重婚或脚踩两条船。
输出格式:
首先第一行输出落单客人的总人数;随后第二行按 ID 递增顺序列出落单的客人。ID 间用 1 个空格分隔,行的首尾不得有多余空格。
输入样例:
3
11111 22222
33333 44444
55555 66666
7
55555 44444 10000 88888 22222 11111 23333
输出样例:
5
10000 23333 44444 55555 88888
答案:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e5 + 10;
using namespace std;
set<int>s;
int dp[N];
int mp[N];
int bp[N];
int main()
{
memset(dp,-1,sizeof(dp));
int n;
cin>>n;
int i;
for(i=1; i<=n; i++)
{
int x,y;
cin>>x>>y;
dp[x]=y;
dp[y]=x;
}
int m;
cin>>m;
for(i=1; i<=m; i++)
{
cin>>mp[i];
if(dp[mp[i]]!=-1)
{
bp[dp[mp[i]]]=1;
}
}
for(i=1; i<=m; i++)
{
if(!bp[mp[i]])
s.insert(mp[i]);
}
int len=s.size();
cout<<len<<endl;
set<int>::iterator it;
for(it=s.begin(); it!=s.end(); it++)
{
if(it==s.begin())
printf("%05d",*it);
else
printf(" %05d",*it);
}
if(len)
printf("\n");
return 0;
}
7-13 寻找大富翁 (25分)
胡润研究院的调查显示,截至2017年底,中国个人资产超过1亿元的高净值人群达15万人。假设给出N个人的个人资产值,请快速找出资产排前M位的大富翁。
输入格式:
输入首先给出两个正整数N(≤10^6 )和M(≤10),其中N为总人数,M为需要找出的大富翁数;接下来一行给出N个人的个人资产值,以百万元为单位,为不超过长整型范围的整数。数字间以空格分隔。
输出格式:
在一行内按非递增顺序输出资产排前M位的大富翁的个人资产值。数字间以空格分隔,但结尾不得有多余空格。
输入样例:
8 3
8 12 7 3 20 9 5 18
输出样例:
20 18 12
答案:
数组位移:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e6 + 10;
using namespace std;
int dp[N];
int mp[11];
void f(int k,int m)
{
int i;
for(i=1; i<=m; i++)
{
if(k>mp[i])
{
if(i==m)
{
mp[i]=k;
break;
}
else
{
int j;
for(j=m; j>i; j--)
{
mp[j]=mp[j-1];
}
mp[j]=k;
break;
}
}
}
}
int main()
{
ios::sync_with_stdio(0);
int n,k;
cin>>n>>k;
int i;
memset(mp,0,sizeof(mp));
for(i=1; i<=n; i++)
{
cin>>dp[i];
f(dp[i],k);
}
if(n>=k)
{
for(i=1; i<=k; i++)
{
if(i==k)
printf("%d\n",mp[i]);
else
printf("%d ",mp[i]);
}
}
else
{
for(i=1; i<=n; i++)
{
if(i==n)
printf("%d\n",mp[i]);
else
printf("%d ",mp[i]);
}
}
return 0;
}
优先队列:
#include <bits/stdc++.h>
using namespace std;
priority_queue<int> p;
int main()
{
int n,m,a;
scanf("%d%d",&n,&m);
int flag=0,flag1=0;
for(int i=0; i<n; i++)
{
scanf("%d",&a);
p.push(a);
}
if(m>n)
{
while(p.size()>=1)
{
if(flag==0)
{
printf("%d",p.top());
p.pop();
flag=1;
}
else
{
printf(" %d",p.top());
p.pop();
}
}
}
else
{
for(int i=0;i<m;i++)
{
if(flag1==0)
{
printf("%d",p.top());
p.pop();
flag1=1;
}
else
{
printf(" %d",p.top());
p.pop();
}
}
}
return 0;
}
7-14 插入排序还是堆排序 (25分)
根据维基百科的定义:
插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。
堆排序也是将输入分为有序和无序两部分,迭代地从无序部分找出最大元素放入有序部分。它利用了大根堆的堆顶元素最大这一特征,使得在当前无序区中选取最大元素变得简单。
现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?
输入格式:
输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。
输出格式:
首先在第 1 行中输出Insertion Sort
表示插入排序、或Heap Sort
表示堆排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。
输入样例 1:
10
3 1 2 8 7 5 9 4 6 0
1 2 3 7 8 5 9 4 6 0
输出样例 1:
Insertion Sort
1 2 3 5 7 8 9 4 6 0
输入样例 2:
10
3 1 2 8 7 5 9 4 6 0
6 4 5 1 0 3 2 7 8 9
输出样例 2:
Heap Sort
5 4 3 1 0 2 6 7 8 9
具体思路:对于这里的插入排序,我们直接从左往有移动,看哪一个先不符合递增,然后再对这一段进行排序就好了(看样例猜的)。
然后堆排序,先讲一下原理吧,具体的大顶堆,就是第一个形成的堆第一个元素是这一段区间中的最大的。然后每一个我们选出最大的,放在最后。如果一个序列有n个元素的话,我们操作n-1次就可以了,每一次从[1,i]区间选择最大的。
答案:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e5 + 10;
using namespace std;
int a[111];
int b[111];
void f(int l,int r)
{
int i=l,j=i*2;
while(j<=r)
{
if(j+1<=r&&b[j]<b[j+1])
j++;
if(b[j]>b[i])
{
swap(b[i],b[j]);
i=j;
j=i*2;
}
else
break;
}
}
int main()
{
int n;
cin>>n;
int i;
for(i=1; i<=n; i++)
{
cin>>a[i];
}
for(i=1; i<=n; i++)
{
cin>>b[i];
}
int book=-1;
for(i=1; i<=n; i++)
{
if(i==1)
continue;
else if(b[i]>=b[i-1])
continue;
else
{
book=i;
break;
}
}
if(book!=2)
{
printf("Insertion Sort\n");
sort(b+1,b+1+book);
for(int i=1; i<=n; i++)
{
if(i==1)
printf("%d",b[i]);
else
printf(" %d",b[i]);
}
printf("\n");
}
else
{
printf("Heap Sort\n");
int pos=n;
while(pos>=2&&b[pos]>b[pos-1])
pos--;
swap(b[1],b[pos]);
f(1,pos-1);
for(int i=1; i<=n; i++)
{
if(i==1)
printf("%d",b[i]);
else
printf(" %d",b[i]);
}
printf("\n");
}
return 0;
}
7-15 PAT排名汇总 (25分)
计算机程序设计能力考试(Programming Ability Test,简称PAT)旨在通过统一组织的在线考试及自动评测方法客观地评判考生的算法设计与程序设计实现能力,科学的评价计算机程序设计人才,为企业选拔人才提供参考标准(网址http://www.patest.cn)。
每次考试会在若干个不同的考点同时举行,每个考点用局域网,产生本考点的成绩。考试结束后,各个考点的成绩将即刻汇总成一张总的排名表。
现在就请你写一个程序自动归并各个考点的成绩并生成总排名表。
输入格式:
输入的第一行给出一个正整数N(≤100),代表考点总数。随后给出N个考点的成绩,格式为:首先一行给出正整数K(≤300),代表该考点的考生总数;随后K行,每行给出1个考生的信息,包括考号(由13位整数字组成)和得分(为[0,100]区间内的整数),中间用空格分隔。
输出格式:
首先在第一行里输出考生总数。随后输出汇总的排名表,每个考生的信息占一行,顺序为:考号、最终排名、考点编号、在该考点的排名。其中考点按输入给出的顺序从1到N编号。考生的输出须按最终排名的非递减顺序输出,获得相同分数的考生应有相同名次,并按考号的递增顺序输出。
输入样例:
2
5
1234567890001 95
1234567890005 100
1234567890003 95
1234567890002 77
1234567890004 85
4
1234567890013 65
1234567890011 25
1234567890014 100
1234567890012 85
输出样例:
9
1234567890005 1 1 1
1234567890014 1 2 1
1234567890001 3 1 2
1234567890003 3 1 2
1234567890004 5 1 4
1234567890012 5 2 2
1234567890002 7 1 5
1234567890013 8 2 3
1234567890011 9 2 4
答案:
#include <iostream>
#include<bits/stdc++.h>
#define ll long long
const int N = 1e6 + 10;
using namespace std;
struct node
{
ll s;
int grade;
int znpm;
int zpm;
int zu;
} p[333];
node dp[31111];
bool cmp(node x,node y)
{
if(x.grade==y.grade)
return x.s<y.s;
return x.grade>y.grade;
}
int main()
{
ios::sync_with_stdio(0);
int n;
cin>>n;
int a[111];
int sum=0;
int i;
for(i=1; i<=n; i++)
{
cin>>a[i];
sum+=a[i];
int j;
for(j=1; j<=a[i]; j++)
{
p[j].zu=i;
cin>>p[j].s>>p[j].grade;
}
sort(p+1,p+1+a[i],cmp);
int cnt=1;
p[1].znpm = 1;
cnt=1;
for(j=2; j<=a[i]; j++)
{
cnt++;
if(p[j].grade== p[j-1].grade)
{
p[j].znpm = p[j-1].znpm;
}
else
{
p[j].znpm = cnt;
}
}
int t=1;
for(j=sum-a[i]+1; j<=sum; j++)
{
dp[j]=p[t++];
}
}
cout<<sum<<endl;
sort(dp+1,dp+1+sum,cmp);
int cnt=1;
dp[1].zpm = 1;
cnt=1;
int j;
for(j=2; j<=sum; j++)
{
cnt++;
if(dp[j].grade== dp[j-1].grade)
{
dp[j].zpm = dp[j-1].zpm;
}
else
{
dp[j].zpm = cnt;
}
}
for(i=1; i<=sum; i++)
{
printf("%013lld %d %d %d\n",dp[i].s,dp[i].zpm,dp[i].zu,dp[i].znpm);
}
return 0;
}
7-16 插入排序还是归并排序 (25分)
根据维基百科的定义:
插入排序是迭代算法,逐一获得输入数据,逐步产生有序的输出序列。每步迭代中,算法从输入序列中取出一元素,将之插入有序序列中正确的位置。如此迭代直到全部元素有序。
归并排序进行如下迭代操作:首先将原始序列看成 N 个只包含 1 个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下 1 个有序的序列。
现给定原始序列和由某排序算法产生的中间序列,请你判断该算法究竟是哪种排序算法?
输入格式:
输入在第一行给出正整数 N (≤100);随后一行给出原始序列的 N 个整数;最后一行给出由某排序算法产生的中间序列。这里假设排序的目标序列是升序。数字间以空格分隔。
输出格式:
首先在第 1 行中输出Insertion Sort
表示插入排序、或Merge Sort
表示归并排序;然后在第 2 行中输出用该排序算法再迭代一轮的结果序列。题目保证每组测试的结果是唯一的。数字间以空格分隔,且行首尾不得有多余空格。
输入样例 1:
10
3 1 2 8 7 5 9 4 6 0
1 2 3 7 8 5 9 4 6 0
输出样例 1:
Insertion Sort
1 2 3 5 7 8 9 4 6 0
输入样例 2:
10
3 1 2 8 7 5 9 4 0 6
1 3 2 8 5 7 4 9 0 6
输出样例 2:
Merge Sort
1 2 3 8 4 5 7 9 0 6
答案:
#include<bits/stdc++.h>
using namespace std;
int a[111],b[111];
int n;
int F()
{
for(int i=0; i<n; i++)
{
if(a[i]!=b[i])
return 0;
}
return 1;
}
int main()
{
scanf("%d",&n);
for(int i=0; i<n; i++)
scanf("%d",&a[i]);
for(int i=0; i<n; i++)
scanf("%d",&b[i]);
int flag=0;
for(int i=2;; i*=2)
{
for(int j=0; j<n; j+=i)
{
if(j+i<n)
sort(a+j,a+j+i);
else
sort(a+j,a+n);
}
if(flag)
{
printf("Merge Sort\n");
for(int i=0; i<n; i++)
printf("%d%c",a[i]," \n"[i==n-1]);
break;
}
if(F())
flag=1;
if(i>n)
break;
}
if(!flag)
{
printf("Insertion Sort\n");
int t=1;
while(t<n&&b[t-1]<=b[t])
t++;
while(t>0&&b[t-1]>b[t])
{
swap(b[t-1],b[t]);
t--;
}
for(int i=0; i<n; i++)
printf("%d%c",b[i]," \n"[i==n-1]);
}
return 0;
}