Loading

CF1437C

Solution:

看到这道题很显然可以想到贪心是假算法,所以我们需要考虑如何使用 \(dp\) 。因为本题需要的是关于时间的,并且要取出物品,所以我们可以设计 \(dp\) 状态为 \(f[i][j]\) 表示在 \(i\) 时刻,取了前 \(j\) 个物品。一共有三种转移状态:

\(f[i][j]=min\begin{cases}f[i][j]\\f[i-1][j]\\f[i-1][j-1]+abs(i-t[j])\end{cases}\)

\(f[i-1][j]\) 表示在 \(i\) 时刻不选第 \(j\) 个物品。\(f[i-1][j-1]+abs(i-t[j])\) 表示在第 \(i\) 个时刻取第 \(j\) 个物品的花费。

但是你如果 \(i\in[1,n]\) 那就会发现第二个样例就跑不过去了,这是因为你不光可以在 \([1,n]\) 中,也可以在更后的时间里,也就是说你的 \(i\in[1,2n]\) 才可以。当然最后输出的时候答案应该是:

\(ans=min(f[i][n])\;i\in[n,2n]\)

最后输出 \(ans\) 即可得出正确答案。

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0' || c>'9'){if(c=='-') f=0;c=getchar();}
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return f?x:-x;
}
int T,n,t[210],f[410][210],ans;
signed main()
{
    T=read();
    while(T--)
    {
        n=read();
        for(int i=1;i<=n;i++) t[i]=read();
        sort(t+1,t+n+1);
        memset(f,0x3f,sizeof(f));
        f[0][0]=0;
        for(int i=1;i<=2*n;i++)
        {
            f[i][0]=0;
            for(int j=1;j<=min(i,n);j++)
            {
                f[i][j]=min(f[i][j],f[i-1][j]);
                f[i][j]=min(f[i][j],f[i-1][j-1]+abs(i-t[j]));
            }
        }
        ans=INT_MAX;
        for(int i=n;i<=2*n;i++) ans=min(ans,f[i][n]);
        printf("%d\n",ans);
    }
    return 0;
}

posted @ 2020-12-21 21:04  ForeverOIer  阅读(107)  评论(0编辑  收藏  举报