版权声明:个人笔记,仅供复习 https://blog.csdn.net/weixin_42373330/article/details/88973807
E: Carryon分糖果
题目描述
Carryon最近在带学弟刷题,让学弟们打了一场比赛,一共有n个学弟,第i个人的分数为a[i],现在想要给他们分一些糖果作为奖励。将他们围成一个圈发糖,但是怕他们心里不平衡就制定了一些规则。
1.每个人只能看到旁边人的分数和糖果数
2.分数高的得到的糖果比分数低的多
3.每个人至少一个糖果
现在想用最少的糖果来解决这个问题,请输出最终所需要的糖果数。
输入
第一行输入T表示有多少组;(T <= 100)
每组输入为一个整数n,表示学弟的个数。(0 < n <= 10000)
接下来输入n个an[i],表示他们所得的分数。(0 < an[i] < 100000)
输出
输出所需最少的糖果数
样例输入
2
20
2 2 6 7 7 6 5 4 3 1 3 5 7 9 10 10 12 10 10 10
13
2 3 4 4 5 6 7 6 7 6 5 3 2
样例输出
55
32
提示
样例解释:
对于第一个样例当糖果按下面分则最少
1 1 2 3 6 5 4 3 2 1 2 3 4 5 6 1 2 1 1 2
所以答案为55
对于第二样例当糖果按下面分则最少
1 2 3 1 2 3 4 1 5 4 3 2 1
所以答案为32
分析:
①每次从最小的开始,如果两边比它大,往两边延伸(类似v字);直到所有的位置都遍历完;
②判断两个v的接触部分,是否符合题目规则,处理下不符合的
代码:
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
#define ll long long
const int N = 1e5+10;
struct p
{
int id;//在a数组中的位置
int value;//在a数组中的值
};
bool cmp(p a,p b)
{
return a.value<b.value;
}
int a[N];
int val[N];
int vis[N];
int t;
int n;
ll sum = 0;
vector<p> v;
int duv[N];//记录左断点位置
ll f()
{
int len=0;
for(int i=0;i<v.size();i++)
{
p x=v[i];
int id = x.id;
if(vis[id]) continue;
sum+=1;
vis[id] = 1;
int k=id;//向右
int j=id;//向左
//往两边延伸,像个v字行
int cnt1=1;
int cnt2=1;
val[id]=1;
while(a[(k+1)%n]>a[k%n]&&!vis[(k+1)%n])
{
cnt1++;
vis[(k+1)%n]=1;
sum+=cnt1;
val[(k+1)%n]=cnt1;
k=(k+1)%n;
}
while(a[(j-1+n)%n]>a[j%n]&&!vis[(j-1+n)%n])
{
cnt2++;
vis[(j-1+n)%n]=1;
sum+=cnt2;
val[(j-1+n)%n]=cnt2;
j=(j-1+n)%n;
}
if(!(a[(k+1)%n]>a[k%n]&&!vis[(k+1)%n])) duv[len++]= k%n;//记录左断点位置
}
for(int i=0;i<len;i++)//判断
{
int le=duv[i];
int ri=(le+1)%n;
if(a[le]>a[ri])
{
if(val[le]<=val[ri]) sum+=val[ri]-val[le]+1;
}
else if(a[le]==a[ri])
{
continue;
}
else //小于
{
if(val[le]>=val[ri]) sum+=val[le]-val[ri]+1;
}
}
return sum;
}
int main()
{
scanf("%d",&t);
while(t--)
{
sum = 0;
scanf("%d",&n);
v.clear();
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
p x;
x.id=i;
x.value=a[i];
v.push_back(x);
}
sort(v.begin(),v.end(),cmp);
cout<<f()<<endl;
}
return 0;
}