• nowcoder 2019noip暑假7天营Day4 T2卖羊驼


    (V)为最大权值,(K)为总段数,(n)为羊驼总数,(p[i])表示第 i 只羊驼的权值,(dp[i][k])表示在 i 及 i 之前将序列分解为k组的最优解,(lp[j])表示在 i 往前第一个二进制第 j 位为1的羊驼 , (or\_val[j]) 为从 (i)(lp[j]) 的或和

    根据如上定义,我们可以得到一个动态转移方程

    [dp[i][k]=max(dp[i][k],dp[lp[j]][k-1]+or\_val[j]) ]

    其中 j 从 0 到 (lceil log_2(V) ceil),时间复杂度为(O(nKlog_2(V)))

    实际上,我们最初的想法或许是这样的(其中i,j,k与上无关)

    (dp[i][j] = max(dp[i][j],dp[k-1][j-1]+or\_sum(k,i)))

    其中 k 为枚举 i 往前的任意一个节点

    可是再望时间复杂度(O(n^2K)),受不起

    我们再想(这是一个绝妙的点),或和的本质是对于每一个二进制位上来说,若其中一个值在这一位上为1,则或和在这一位上也为1。而对于一个节点 i 来说,若有两个节点 a , b ((a<b)),它们的值均在某一位上为1,那么选择 b 作为 新阶段的开始,会比 a 更优。

    而对于 第二个转移方程中的 k 的枚举,本质上是对二进制位的枚举,以求出最优答案。

    因而,我们会有如下代码

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int const MAXN=5010,MAXK=1010;
    int n,K;
    int p[MAXN],dp[MAXN][MAXK],lp[MAXN],or_val[MAXN];
    signed main(){
    	scanf("%lld%lld",&n,&K);
    	for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<=24;j++){//24
    			if((p[i]>>j)&1){
    				or_val[j]=p[i];
    				lp[j]=i;
    			}
    			or_val[j]|=p[i];
    		}
    		for(int k=1;k<=K && k<=i;k++){
    			for(int j=0;j<=24;j++){
    				if(lp[j]>=k)dp[i][k]=max(dp[i][k],dp[lp[j]-1][k-1]+or_val[j]);
    			}
    		}
    	}
    	printf("%lld
    ",dp[n][K]);
    	return 0;
    }
    

    本代码对(or\_val,lp)采取的是在 dp 中更新

    而还有另外一种玩法,就是在 dp 前就预处理好,这时对于 (or\_val[i][j]) 的处理便可以用ST表我绝不是因为我不会才没有写

  • 相关阅读:
    [LeetCode]题解(python):053-Maximum Subarray
    [LeetCode]题解(python):052-N-Queens II
    [LeetCode]题解(python):051-N-Queens
    [LeetCode]题解(python):050-Pow(x, n)
    [LeetCode]题解(python):049-Group Anagrams
    [LeetCode]题解(python):048-Rotate Image
    构建之法阅读笔记01
    软件工程第一周开课博客
    第二周作业:返回一个整数数组中最大子数组的和
    第二周javaweb学习进度表
  • 原文地址:https://www.cnblogs.com/fpjo/p/11405548.html
Copyright © 2020-2023  润新知