• Codeforces 1077(F1+F2) DP 单调队列


    题意:给你一个n个元素的数组,从中选取x个元素,并且要保证任意的m个位置中必须至少有一个元素被选中,问选中元素的和最大可以是多少?

    F1 n,m,x到200 F2 n,m,x到5000。

    思路1:设dp[i][j]为选择i位置的元素,并且包括i位置已经选择了j个元素,所有选中元素的最大和。

    那么为了保证方案的合法性,只能从i-m+1的地方状态转移,dp[i][j]=max(dp[k][j-1])+a[i] (i-m+1<=k<i);,意思是找从i-m+1位置到i-1位置找已经选区j-1个元素中和最大的一个,在加上自己。

    最后在n-m+1到i中找最大值就是答案,这些点因为已经被选取,所以保证了最终答案的合法性。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    long long dp[210][210],a[210];
    int main(){
    	int n,m,x;
    	scanf("%d%d%d",&n,&m,&x);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    	}
    	int tmp=n-(m-1);
    	if((tmp/m+(tmp%m!=0))>x){
    		printf("-1
    ");
    		return 0;
    	}
    	int lim=1;
    	for(int i=1;i<=n;i++){
    		for(int j=lim;j<=min(i,x);j++){
    			for(int k=max(0,i-m);k<i;k++){
    				dp[i][j]=max(dp[k][j-1]+a[i],dp[i][j]);
    			}
    		}
    		if(i%m==0)lim++;
    	}
    		
    	long long ans=0;
    	for(int i=n-m+1;i<=n;i++)
    		ans=max(ans,dp[i][x]);
    	printf("%lld
    ",ans);
    } 
    

    思路2:现在数据规模变大了,原来的做法可以卡成O(n3),过不了。仔细观察状态转移过程,会发现有很多无用的状态,比如dp[i][j-1]<dp[k][j-1] (i<k),dp[i][j-1]这个状态肯定不会成为更新的跳板,于是我们就可以用单调队列优化。

    外层循环枚举选取的元素个数,内层循环枚举数组的元素,用单调队列的队头更新最优解,此时dp[i][j]表示已经选区了i个元素,第j个元素已经被选的最优解,单调队列的队头出队过程保证了方案的合法性。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    long long dp[2][5010],a[5010];
    int q[100010],l=1,r=0; 
    int main(){
    	int n,m,x;
    	scanf("%d%d%d",&n,&m,&x);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    	}
    	int tmp=n-(m-1);
    	if((tmp/m+(tmp%m!=0))>x){
    		printf("-1
    ");
    		return 0;
    	}
    	memset(dp,-0x3f,sizeof(dp));
    	dp[1][0]=0;
    	int now=0;
    	for(int i=1;i<=x;i++){
    		l=1,r=0;
    		q[++r]=0;
    		for(int j=1;j<=n;j++){
    			while(l<=r&&q[l]+m<j)l++;
    			dp[now][j]=dp[now^1][q[l]]+a[j];
    			while(l<=r&&dp[now^1][q[r]]<=dp[now^1][j])r--;
    			q[++r]=j;
    		}
    		now^=1;
    	}
    	now^=1;
    	long long ans=-1;
    	for(int i=n-m+1;i<=n;i++)
    		ans=max(ans,dp[now][i]);
    	printf("%lld
    ",ans);
    } 
    

      

    ---恢复内容结束---

  • 相关阅读:
    python 发送邮件 email
    python 日志 logging
    python 判断当前环境是什么系统 platform 模块
    pandas to_excel 添加颜色
    Pandas excel 双标题 多级索引 层次化索引 MultiIndex
    windows10 安装 pyltp python3.6
    Python的内存管理
    pandas 中 DataFramt 改变 列的顺序
    元组
    基本语法
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/9974886.html
Copyright © 2020-2023  润新知