zoukankan      html  css  js  c++  java
  • 【洛谷P1430】序列取数【dp】

    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P1430
    给定一个长为n的整数序列,由A和B轮流取数(A先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分。


    思路:

    时限3s3s,显然是要一个常数小的O(Tn2)O(Tn^2)的方法。
    由于每次A和B都只可以在左右两端取数,所以取完后剩余的一定是一个连续的子区间。
    正序做似乎不是很好做,考虑倒序,将问题转化成一个区间问题。
    先考虑最暴力的方法,若先手取时剩余区间为[l,r][l,r],那么后手一定是取了[x,r][x,r][l,x](l<x<r)[l,x](l< x < r),然后先手取[l,x1][l,x-1][x+1,r][x+1,r]
    dp[i][j]dp[i][j]表示先手取成[l,r][l,r]的最优答案,那么这个答案一定由后手的最劣方案转移而来。因为若后手取后的区间和为ss',区间[l,r][l,r]的和为ss,那么先手取的和为sss-s'。我们要让sss-s'尽量大,那么就让ss'尽量小即可。即后手的最劣方案。
    所以转移方程就是
    dp[i][j]=sum[j]sum[i1]min(0,dp[i][i],dp[i][i+1],...,dp[i][j1],dp[j][j],dp[j1][j],...,dp[i+1][j])dp[i][j]=sum[j]-sum[i-1]-min(0,dp[i][i],dp[i][i+1],...,dp[i][j-1],dp[j][j],dp[j-1][j],...,dp[i+1][j])
    其中sum[i]sum[i]a[i]a[i]的前缀和,sum[j]sum[i1]sum[j]-sum[i-1]k=ija[k]sum^{j}_{k=i}a[k];加上0的原因是因为先手可以一次把所有选完,那么后手就不能选,即选的和为0。
    这样单次询问的复杂度是O(n3)O(n^3)的。
    看到方程中有minminminmin需要O(n)O(n)的复杂度,但是dpdp中的minmin一般都是可以进行转移的。所以可以通过转移minmin来去掉这个O(n)O(n)
    f[i][j]=min(dp[i][i],dp[i][i+1],...,dp[i][j]),g[i][j]=min(dp[j][j],dp[j1][j],...dp[i][j])f[i][j]=min(dp[i][i],dp[i][i+1],...,dp[i][j]),g[i][j]=min(dp[j][j],dp[j-1][j],...dp[i][j])
    方程就变成了
    dp[i][j]=sum[j]sum[i1]min(0,f[i+1][j],g[i][j1])dp[i][j]=sum[j]-sum[i-1]-min(0,f[i+1][j],g[i][j-1])
    f,gf,g的维护都是最最最最基础的了。显然有f[i][j]=min(f[i+1][j],dp[i][i])f[i][j]=min(f[i+1][j],dp[i][i])gg同理。
    这样单次询问的复杂度就降到了O(n2)O(n^2)。总时间复杂度就是O(Tn2)O(Tn^2)。而且区间dpdp的常数好像还是12frac{1}{2}


    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=1010;
    int T,n,a[N],sum[N],dp[N][N],f[N][N],g[N][N];
    
    int main()
    {
    	scanf("%d",&T);
    	while (T--)
    	{
    		scanf("%d",&n);
    		for (int i=1;i<=n;i++)
    		{
    			scanf("%d",&a[i]);
    			sum[i]=sum[i-1]+a[i];
    			dp[i][i]=f[i][i]=g[i][i]=a[i];
    		}
    		for (int i=n-1;i>=1;i--)
    			for (int j=i+1;j<=n;j++)
    			{
    				dp[i][j]=sum[j]-sum[i-1]-min(0,min(f[i+1][j],g[i][j-1]));
    				g[i][j]=min(g[i][j-1],dp[i][j]);
    				f[i][j]=min(f[i+1][j],dp[i][j]);
    			}
    		printf("%d
    ",dp[1][n]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    PostgreSQL备份工具-pg_probackup
    Multi-Master Replication Solutions for PostgreSQL
    PostgreSQL高可用:多主复制解决方案
    Postgressql高可用(pgpool+异步流复制)转
    AudioFlinger
    GPIO口配置为上拉,下拉输入
    转【Qualcomm高通音频】音效调试_录音文件播放有杂音,如何定位原因?
    转【Qualcomm高通音频】如何使用QXDM、QCAT、CoolEditor音频日志抓取、解析和分析?
    转【Qualcomm高通音频】调试工具QACT_如何新增一套音效
    转【Qualcomm高通音频】调试工具QACT_如何更换音效的音频拓扑
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998136.html
Copyright © 2011-2022 走看看