CF1703G

Good Key, Bad Key - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

根据题意:我们可以设 DP[ i ][ j ] 表示前 i 个箱子用了 j 把钥匙

由于 1n105 ,所以第二位不可能这么大

由 1e9<2^31 所以用了31把钥匙之后,每个箱子的价值就一定是0了,所以第二位开35即可

DP[ i ][ j ] 可以由DP[ i-1 ][ j-1 ] 和DP[ i-1 ][ j ]转移过来,线性DP就好了

Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   
#define popb pop_back 
#define fi first
#define se second
const int N=1e5+10;
//const int M=;
//const int inf=0x3f3f3f3f;     
//const ll INF=0x3ffffffffffff; 
int T,n,k;
ll a[N],dp[N][40];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    T=read();
    while(T--)
    {
        n=read(),k=read();
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=0;i<=n;i++)
            for(int j=0;j<=31;j++) dp[i][j]=-1e18;
        dp[0][0]=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=35;j++)
            {
                if(j==0) dp[i][j]=dp[i-1][j]+a[i]-k;
                else dp[i][j]=max(dp[i-1][j]+(a[i]/(1LL<<j))-k,dp[i-1][j-1]+(a[i]/(1LL<<j)));
            }
        }
        ll ans=0;
        for(int i=0;i<=35;i++) ans=max(ans,dp[n][i]);
        printf("%lld\n",ans);
    }
    return 0;
}
 

但你会发现这份代码一直 WA4

所以重点来了,问题出在第二位,实际上 n 偏大的时候,此时会 用多于 31把 钥匙的可能,所以不能仅仅在DP[ n ] [ 0~31 ]中查找;因为有可能后面的全是零,只能使用坏钥匙,这样次数就超过了数组中存的最大坏钥匙个数(31) 

for(int i=0;i<=35;i++) ans=max(ans,dp[n][i]);
 所以这步是错的,到 35 是不合理的。
那么又该如何写为正确答案呢?
DP[ i ]表示取到第 i 个箱子的最大值,显然有 DP[ 1 ]<=DP[ 2 ]<=DP[ 3 ]<=......<=DP[ n ]。
因此,我只需要取 max(dp[i][j]) 即可
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   //vector函数
#define popb pop_back  //vector函数
#define fi first
#define se second
const int N=1e5+10;
//const int M=;
//const int inf=0x3f3f3f3f;     //一般为int赋最大值,不用于memset中
//const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中
int T,n,k;
ll a[N],dp[N][40];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    T=read();
    while(T--)
    {
        n=read(),k=read();
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=0;i<=n;i++)
            for(int j=0;j<=31;j++) dp[i][j]=-1e18;
        dp[0][0]=0;
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=31;j++)
            {
                if(j==0) dp[i][j]=dp[i-1][j]+a[i]-k;
                else dp[i][j]=max(dp[i-1][j]+(a[i]/(1LL<<j))-k,dp[i-1][j-1]+(a[i]/(1LL<<j)));
                ans=max(ans,dp[i][j]); //每次都要更新答案,因为有可能后面的全是零,只能使用坏钥匙,这样次数就超过了数组中存的最大坏钥匙个数(31)  
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2023-01-29 23:25  QAQ啥也不会  阅读(12)  评论(0编辑  收藏  举报