练习地址
糕点
小团的蛋糕铺长期霸占着美团APP中“蛋糕奶茶”栏目的首位,因此总会吸引各路食客前来探店。
小团一天最多可以烤n个蛋糕,每个蛋糕有一个正整数的重量。
早上,糕点铺已经做好了m个蛋糕。
现在,有一个顾客要来买两个蛋糕,他希望买这一天糕点铺烤好的最重的和最轻的蛋糕,并且希望这两个蛋糕的重量恰好为a和b。剩余的n-m个蛋糕可以现烤,请问小团能否满足他的要求?
输入描述:
输入包含多组数据,每组数据两行。每组数据的第一行包含4个整数,n,m,a,b,空格隔开。这里不保证a和b的大小关系。接下来一行m个数,空格隔开,代表烤好的蛋糕重量
输出描述:
对于每一组数据,如果可以办到顾客的要求,输出YES,否则输出NO
输入例子1:
4 2 2 4
3 3
4 2 2 4
1 1
4 2 2 4
5 5
4 2 4 2
2 4
2 2 2 4
3 3
3 2 2 4
3 3
3 2 2 4
3 3
输出例子1:
YES
NO
NO
YES
NO
NO
NO
思路:
由于输入的a和b不保证大小,因此需要处理一下,当a>b时,交换a和b,保证a小b大。
接着在输入已烤好蛋糕时记录最大值和最小值。
而后只需要考虑没烤好蛋糕的情况,共有三种情况:
(1)今天的面包都烤好了,此时只要a和最小值相等以及b和最大值相等即可;
(2)今天的面包还剩一个没烤,此时只要a或b中的一个重量满足最小值或最大值即可,剩下还没烤的一个可以满足要求;
(3)今天的面包多于两个没烤,此时只要a比最小值大且b比最大值小即可,剩下还没烤的面包可以满足要求。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,a,b;
while(cin>>n>>m>>a>>b)
{
vector<int> w(m);
int rest=n-m;
bool flag=0;
if(a>b)
swap(a,b);
int maxv,minv;
for(int i=0;i<m;i++)
{
cin>>w[i];
if(i==0)
maxv=w[i],minv=w[i];
maxv=max(maxv,w[i]);
minv=min(minv,w[i]);
}
if(rest==0&&a==minv&&b==maxv)
flag=1;
if(rest==1&&(a==minv||b==maxv))
flag=1;
if(rest>=2&&minv>=a&&maxv<=b)
flag=1;
if(flag==1)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
晋级人数
小团是某综艺节目的策划,他为某个游戏环节设计了一种晋级规则,已知在这个游戏环节中每个人最后都会得到一个分数score_i,显而易见的是,游戏很有可能出现同分的情况,小团计划该环节晋级人数为x人,则将所有人的分数从高到低排序,所有分数大于等于第x个人的分数且得分不为0的人都可以晋级。
请你求出本环节的实际晋级人数。显然这个数字可能是0,如果所有人的得分都是0,则没有人满足晋级条件。
输入描述:
输入第一行包含两个正整数n和x,分别表示参加本环节的人数,和小团指定的x。输入第二行包含n个整数,每个整数表示一位选手的得分。
输出描述:
输出仅包含一个整数,表示实际晋级人数。
输入例子1:
5 4
0 0 2 3 4
输出例子1:
3
思路:
对参加人数的分数进行降序排序,当得分比第x个人的得分高且得分不为0时,可以晋级。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,x;
cin>>n>>x;
vector<int> s(n);
int cnt=0;
for(int i=0;i<n;i++)
{
cin>>s[i];
}
sort(s.begin(),s.end(),greater<int>());
for(int i=0;i<n;i++)
{
if(s[i]>=s[x-1]&&s[i]!=0)
cnt++;
}
cout<<cnt;
return 0;
}
回转寿司
小美请小团吃回转寿司。转盘上有N盘寿司围成一圈,第1盘与第2盘相邻,第2盘与第3盘相邻,…,第N-1盘与第N盘相邻,第N盘与第1盘相邻。小团认为第i盘寿司的美味值为A[i](可能是负值,如果小团讨厌这盘寿司)。现在,小团要在转盘上选出连续的若干盘寿司,使得这些寿司的美味值之和最大(允许不选任何寿司,此时美味值总和为0)。
输入描述:
第一行输入一个整数T(1<=T<=10),表示数据组数。每组数据占两行,第一行输入一个整数N(1<=N<=105); 第二行输入N个由空格隔开的整数,表示A[1]到A[N](-104<= A[i] <=104)。
输出描述:
每组数据输出占一行,输出一个整数,表示连续若干盘寿司的美味值之和的最大值。
输入例子1:
1
4
3 -2 4 -1
输出例子1:
6
例子说明1:
美味值之和最大连续若干盘寿司为第3盘、第4盘和第1盘。
思路:
成环的最大连续子数组和问题。
(1)不考虑环形,求得连续子数组的最大和及最小和;
(2)只考虑环形求得最大和及最小和。
如果不考虑环形求得的连续子数组的和达到了最小,那么总和减去这个最小值就等于环形情况的最大值了。
题中所求的最大美味值就是(1)和(2)中大的那一个。
理解第二种情况:首尾相连之后如果下一个A[i]增加贡献,可能会出现首尾相连之后连续和更大,对于这种情况直接按照原来方法求一遍连续子数组和最小,然后用所有数的和(sum)减去这个连续子数组的最小值和原来求连续子数组的全局最大值取一个max即可。原因在于sum求的是所有的数的和,这中间不乏计算了连续子数组和为负数的情况,因此将这个最小连续子数组和求出来,再用sum来减掉这个最小连续子数组和就成了首尾相连最大的情况。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
vector<int> a(n);
int sum=0;
for(int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
}
int maxs=a[0],curmax=a[0];
int mins=a[0],curmin=a[0];
for(int i=1;i<n;i++)
{
curmax=max(a[i],a[i]+curmax);
maxs=max(maxs,curmax);
curmin=min(a[i],a[i]+curmin);
mins=min(mins,curmin);
}
cout<<max(sum-mins,maxs)<<endl;
}
return 0;
}
神秘的苹果树
小团找到一颗有n个节点的苹果树,以1号节点为根,且每个节点都有一个苹果,苹果都有一个颜色,但是这棵树被施加了咒术,这使得小团只能从某一个节点的子树中选取某一种颜色的拿。小团想要拿到数量最多的那种颜色的所有苹果,请帮帮她。每次她会指定一个节点t,如果小团只能从节点t的子树中选取某一种颜色的苹果,选取什么颜色能拿到最多的苹果?如果有多种颜色都可以拿同样多的苹果,输出颜色编号最小的那个对应的编号。
节点x的子树定义为所有将x当作祖先的节点,x也视为x的子树的一部分。
输入描述:
第一行一个正整数n表示这颗树上节点的个数。
接下来n-1行,每行两个正整数xi,yi,表示树上第i条边连接的两个节点。
接下来一行n个正整数ci,分别表示从1~n号节点上的苹果的颜色。
接下来一行一个正整数q,表示接下来有q次独立的询问。
接下来q行,每行一个正整数t表示询问:如果小团只能从节点t的子树中选取某一种颜色的苹果,选取什么颜色能拿到最多的苹果?如果有多种颜色都可以拿同样多的苹果,输出颜色编号最小的那个对应的编号。
对于100%的数据n≤5000, 1≤xi,yi,t≤n, ci≤1000000000,q≤1000
输出描述:
输出q行,每行一个整数,表示答案。
输入例子1:
7
1 2
1 3
2 4
2 5
3 6
3 7
1 1 2 1 2 2 3
7
1
2
3
4
5
6
7
输出例子1:
1
1
2
1
2
2
3
思路:
参考评论区中FengxiangHuang的思路:
因为颜色会重复,因此用multiset来存储每个节点的颜色集合,该集合可以通过递归得到,节点x的颜色集合为节点x所有子节点颜色集合相加再加上节点x的颜色。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> e[5100];
int c[5100], res[5100];
set<int> st;
multiset<int> mt[5100];
void dfs(int root, int fa)
{
for (auto i:e[root])
{
if (i == fa)continue;
dfs(i, root), mt[root].insert(mt[i].begin(), mt[i].end());
}
mt[root].insert(c[root]);
int mx = 0, cat;
for (auto i:st)
{
int num = mt[root].count(i);
if (num > mx)
mx = num, cat = i;
}
res[root] = cat;
}
int main()
{
int n, x, y, q, m;
cin >> n;
for (int i = 1; i <= n - 1; ++i)
{
cin >> x >> y;
e[x].push_back(y), e[y].push_back(x);
}
for (int i = 1; i <= n; ++i)
{
cin >> c[i];
st.insert(c[i]);
}
dfs(1, -1);
cin >> q;
for (int i = 0; i < q; ++i)
{
int t;
cin >> t;
printf("%d\n", res[t]);
}
return 0;
}
总结
第四题:
- C++ STL中
multiset
:关联容器,已排序好的集合,允许有相同元素。- 头文件:
#include<set>
- 创建multiset对象:
multiset<T> mt;
- 插入元素:
mt.insert(begin,end);
将区间[begin,end)所有的元素插入mt中,无返回值。mt.insert(x);
插入一个x副本,返回新元素位置,无论插入成功与否。mt.insert(pos,x);
插入一个x元素副本,返回新元素位置,pos为搜索起点,提升插入速度。
- 删除元素:
mt.erase(begin,end);
移除区间[begin,end)的所有元素,无返回值。mt.erase(x);
删除与x相等的所有元素,返回被移除的元素个数。mt.erase(pos);
移除迭代器pos所指位置元素,无返回值。
- 清空集合:
mt.clear();
移除所有元素,将容器清空。 - 返回元素值为x的个数:
mt.count(x);
- 查找元素值为x的第一个元素:
mt.find(x);
如果没有返回end()。 - 返回元素数量:
mt.size();
- 头文件: