D - Forbidden Difference

题目链接:https://atcoder.jp/contests/abc403/tasks/abc403_d

题意:

给定一些数,要求删去一些数使得任意一个数x都不能存在x+d,x-d
求删除操作最少次数为多少?

思路:

通过桶来记录每个数的出现次数,那么对于起点为[0,d-1]的模序列(即每次+d,易知包含了所有情况)

我们需要做的是,对于每一个不同的数开头的模序列,维护每个数大小相差超过d

有一种思想是只删除奇数或偶数位置上的数

然而当一个模序列是这样子的:
d=1
num: 1 2 3 4 5 6
次数: 100 1 1 1 1 100

发现只删除奇数:100 + 1 + 1
只删除偶数: 1 +1 100
而最优的情况为删除中间4个数

(即可以连续删除一些数,而非每次跳一个删除)
所以我们需要用dp做
对于每一个序列,记dp[i][0]:遍历到i,且不删除i
dp[i][1]:遍历到i,且选i
dp[i][0] <- dp[i-1][1]
dp[i][1] <- min:dp[i-1][1],dp[i-1][0] + ..

需要注意的是:
1.d需要特判0
2.每个模序列如果中途某个数计数为0,那么这一段就要单独做dp
3.最后循环结束时也需要判断是否做不做dp

int cnt[maxn];
void solve(){
    int n,d;cin>>n>>d;
    rep(i,1,n){
        int x;cin>>x;
        cnt[x]++;
    }
    int ans=0;
    if(d==0){
        for(int i=0;i<=1e6;i++){
            if(cnt[i])ans+=cnt[i]-1;
        }
        cout<<ans<<endl;return;
    }
    for(int i=0;i<=d-1;i++){
        vector<int>x;
        for(int j=i;j<=1e6;j+=d){
            if(cnt[j]){
                x.pb(cnt[j]);
            }else{
                if(x.size()<=1){
                    x.clear();continue;
                }
                vector<vector<int>>dp(x.size(),vector<int>(2,llmax));
                for(int p=0;p<x.size();p++){
                    if(p==0){
                        dp[p][0]=0;
                        dp[p][1]=x[p];
                    }else{
                        dp[p][0]=dp[p-1][1];
                        dp[p][1]=min(dp[p-1][0],dp[p-1][1])+x[p];
                    }
                }
                ans+=min(dp[x.size()-1][1],dp[x.size()-1][0]);
                x.clear();
            }
        }
        
        if(x.size()>1){
            vector<vector<int>>dp(x.size(),vector<int>(2,llmax));
                for(int p=0;p<x.size();p++){
                    if(p==0){
                        dp[p][0]=0;
                        dp[p][1]=x[p];
                    }else{
                        dp[p][0]=dp[p-1][1];
                        dp[p][1]=min(dp[p-1][0],dp[p-1][1])+x[p];
                    }
                }
                ans+=min(dp[x.size()-1][1],dp[x.size()-1][0]);
                x.clear();
        }
    }
    cout<<ans<<endl;
}
posted @ 2025-04-29 18:52  Marinaco  阅读(70)  评论(0)    收藏  举报
//雪花飘落效果