牛客题解 | 队列得分
题目
题解:
考察点: 动态规划,分类讨论
易错点:
题目中指出\(Pi.set=P(i+1).set\),而\(P\)又是取自\(S\)队列的,因此假设当前位于\(S\)队列当中的位置\(i\),则\(i\)之前的任何位置都可能成为其在\(P\)中的上一个位置
解法一:动态规划
设\(dp[i]\)表示在队列\(S\)中\(1-i\)位置\(Score\)的最大值,\(num[i]\)表示元素个数的最小值。根据在易错点中的分析\(i\)之前的任意一个位置都可能成为新队列\(P\)中位置\(i\)的上一个位置,因此\(dp[i]\)由所有比它小的位置和\(i\)的作用转移过来,也即是\(dp[i]=max_{j<i}(dp[j]-(p[i].set==p[j].set?10:0))\)。同时由于位于后面的位置一定不可能得到在元素更少的情况下拥有和前面位置相同的值,因此\(num[i]\)直接由\(num[j]\)转移过来就好了,时间复杂度为\(o(n^2)\) cpp #include "bits stdc++.h" using namespace std; const int maxn="500+10;" struct node{ set,value; }p[maxn]; n,dp[maxn],num[maxn]; main() { freopen("in.txt","r",stdin); scanf("%d",&n); for(int i="1;" scanf("%d%d",&p[i].set,&p[i].value); } mx="dp[i];" tmp="dp[j]-(p[i].Set==p[j].Set?10:0);" j="1;j<i;j++){" if(tmp<dp[j]-(p[i].set="=p[j].Set?10:0)){" cur="num[j];" dp[i]="tmp+p[i].Value;" num[i]="cur+1;" if(mx<dp[i]){ mxnum="num[i];" printf("%d %d\n",mx,mxnum); return 0; 解法二:分类讨论 认真分析题目,不难发现,对于选取的\(p\)队列中连续且\(set\)相同元素具有如下性质: \(1.\)如果这一段里面的所有元素都小于等于\(10\),则只能从中选出\(1\)个元素,否则会起反作用,因此毫无疑问应该选取\(value\)值最大的那个 \(2.\)如果这一段中存在大于\(10\)的,则只选取所有大于\(10\)的部分,并统计个数 基于上述性质,只需分类讨论每个位置和\(10\)的关系,同时对于连续且\(set\)相同的元素,看作一个\(group\)合在一起处理,对于\(group\)内的元素应用性质\(1\)和性质\(2\)进行分类讨论即可,时间复杂度\(o(n)\) n; sum="0,num=0;" while(i<="n){" pos="i;" cnt="0,sum10=0,ans=0;" while(pos<="n&&p[pos].Set==p[i].Set){" if(p[pos].value<="10){" ans="max(ans,p[pos].Value);" else{ sum10+="p[pos].Value;" cnt++; pos++; if(cnt>0){
sum+=sum10-((cnt-1)*10);
num+=cnt;
}else{
sum+=ans;
num+=1;
}
i=pos;
}
printf("%d %d\n",sum,num);
return 0;
}
</i}(dp[j]-(p[i].set==p[j].set?10:0))$。同时由于位于后面的位置一定不可能得到在元素更少的情况下拥有和前面位置相同的值,因此$num[i]$直接由$num[j]$转移过来就好了,时间复杂度为$o(n^2)$>

浙公网安备 33010602011771号