CF1703G
Good Key, Bad Key - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
根据题意:我们可以设 DP[ i ][ j ] 表示前 i 个箱子用了 j 把钥匙
由于 1≤n≤105 ,所以第二位不可能这么大
由 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; }