题解:ABC 277 D - Takahashi's Solitaire
·题目
链接:Atcoder。
链接:洛谷。
·难度
算法难度:入门。
思维难度:提高。
调码难度:入门。
综合评价:简单。
·算法
贪心+前缀和+动态规划
·思路
将卡牌按照除以m的余数从大到小进行排序,利用动态规划求出f[i],即选取i为倒数第一个牌,前面(包括自己)最多可以取几个,当然,由于是按照除以m的余数从大到小进行排序的,在选取纸牌的过程中按照排序后的a顺序依次选取(n号纸牌的下一个是1号纸牌)直到下一个纸牌不符合要求为止,在选取的过程中f自然就求出来了,至于从哪个开始遍历取牌,随便找一个和上一张纸牌不符合要求的即可。
具体过程:先排序,从左往右找到第一个和上一张牌不符合要求的,从这张牌开始遍历(遍历顺序:该纸牌,该纸牌+1,该纸牌+2,......,n,1,2,3,......,该纸牌-1),记这张牌为id,若目前还没有遇到不合法的情况,f[id]=1,f[id+1]=2,f[id+2]=3......若遇到一张纸牌不合法,那么就更新id为当前纸牌,与前面操作相同(但是实际上只需要把这个f设成1,后面的依次+1就行了,而不需要实际更改变量id的值)。最后,通过前缀和求出每个i(1<=i<=n)的f[i]所对应选取情况的纸牌上写的数的总和,并打擂台求出每个总和的最大值,用所有纸牌上的数的和减去最大值就是答案。
·代价
O(n)。无论是什么都只会遍历一层。
·细节
对于求和操作中,如果用到n后面接了1,求和时需要特殊求(sum函数)。
·代码
cpp
#include<bits/stdc++.h>
#define N 220000
using namespace std;
long long s[N]={},ans=0;
int a[N]={},f[N]={},m=0,n=0;
long long sum(int l,int r);
bool cmp(int x,int y);
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
s[i]=s[i-1]+a[i];
}
int id=-1;
for(int i=1;i<=n&&id==-1;i++){
int ot=i-1;
if(ot==0){
ot=n;
}
int mod1=a[i]%m,mod2=a[ot]%m;
if((mod2+m-mod1)%m>1){
id=i;
break;
}
}
if(id==-1){
printf("0\n");
return 0;
}
int lst=-1,now=1;
for(int i1=id,i2=1;i2<=n;i1=(i1==n?1:i1+1),i2++){
if(lst==-1){
f[i1]=1;
}else{
int x=a[lst]%m,y=a[i1]%m;
if((x+m-y)%m>1){
now=1;
}
f[i1]=now;
}
lst=i1;
now++;
}
for(int i=1;i<=n;i++){
int c=f[i]-1,t=i,z=0;
if(t-c<=0){
c-=t;
t=n;
}
z=t-c;
ans=max(ans,sum(z,i));
}
printf("%lld\n",sum(1,n)-ans);
return 0;
}
long long sum(int l,int r){
if(l<=r){
return s[r]-s[l-1];
}
return sum(l,n)+sum(1,r);
}
bool cmp(int x,int y){
return x%m>y%m;
}
·注意
开longlong。