• CF771E Bear and Rectangle Strips


    一、题目

    点此看题

    二、解法

    首先设计出暴力 (dp),设 (dp[i][j]) 表示第一行考虑到 (i),第二行考虑到 (j) 的最大得分,先写转移:

    • 扩展第一行,可以无得分让 (i+1);可以选从 (i+1) 开始的和为 (0) 的段(选右端点最小的)
    • 扩展第二行,可以无得分让 (j+1),可以选从 (j+1) 开始的和为 (0) 的段。
    • (i=j) 时,同时扩展两行,选一个 (i+1) 开始的何为 (0) 的矩阵。

    优化的关键是只有 (i=j) 时两行的转移才有交叉,否则两行的转移是相对独立的。一个至关重要的 ( t observation) 是当 (i ot=j) 时,我们只用扩展较小的那一行(指 (i,j) 比较出来较小)。因为如果不考虑第三种转移那么先扩展哪一行都是没关系的,否则扩展较小的一行就更有利于考虑第三种转移。

    那么我们以 (dp[i][i]) 为转移主体,也就是考虑两行前 (i) 列的最大得分,对于 (i ot=j) 的扩展,我们只需要找到最小的 (j) 使得 (dp[i][j]=dp[i][i]+1)(或者 (dp[j][i]=dp[i][i]+1)),因为如果 (dp[i][j]>dp[i][i]+1) 那么说明某一行扩展多了,不符合上述结论所以自然不用考虑。

    转移的时候维护那个 (j) 即可,时间复杂度 (O(n))

    三、总结

    考虑有效转移是优化的重要方法,也就是如果某一种转移的作用会在以后被解决那么我们就不用考虑它。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <map>
    using namespace std;
    const int M = 300005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,a[2][M],nx[3][M],s[3],dp[M];map<int,int> mp[3];
    struct node
    {
    	int x,y,c;
    };vector<node> vc[M];
    void add(int x,int y,int c)
    {
    	dp[max(x,y)]=max(dp[max(x,y)],c);
    	vc[min(x,y)].push_back(node{x,y,c});
    }
    void extend(int x,int y,int c)
    {
    	if(x<n)
    	{
    		add(x+1,y,c);
    		int i=nx[0][x+1];
    		if(i) add(i,y,c+1);
    	}
    	if(y<n)
    	{
    		add(x,y+1,c);
    		int i=nx[1][y+1];
    		if(i) add(x,i,c+1);
    	}
    	if(x<n && x==y)
    	{
    		int i=nx[2][x+1];
    		if(i) add(i,i,c+1);
    	}
    }
    signed main()
    {
    	n=read();
    	for(int i=0;i<2;i++)
    		for(int j=1;j<=n;j++)
    			a[i][j]=read();
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=0;j<3;j++)
    			mp[j][s[j]]=i;
    		s[0]+=a[0][i];
    		s[1]+=a[1][i];
    		s[2]+=a[0][i]+a[1][i];
    		for(int j=0;j<3;j++)
    			if(mp[j][s[j]]) nx[j][mp[j][s[j]]]=i;
    	}
    	for(int i=0;i<n;i++)
    	{
    		extend(i,i,dp[i]);
    		int l=n+1,r=n+1;
    		for(node a:vc[i])
    		{
    			if(a.y==i && a.c==dp[i]+1)
    				l=min(l,a.x);
    			if(a.x==i && a.c==dp[i]+1)
    				r=min(r,a.y);
    		}
    		if(l<=n) extend(l,i,dp[i]+1);
    		if(r<=n) extend(i,r,dp[i]+1);
    	}
    	printf("%lld
    ",dp[n]);
    }
    
  • 相关阅读:
    pdf.js-----后端返回utf-8数据流,前端处理数据展示pdf
    正则表达式之去除前后空格
    ng之邮箱校验
    ng-校验重复并提示具体重复内容
    input 数值框处理
    逻辑之不重复
    ng -----监听变化($scope.$watch())
    js中document的用法
    php中12个魔术方法
    php 中const和 define的区别
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14999074.html
Copyright © 2020-2023  润新知