zoukankan      html  css  js  c++  java
  • CF559E Gerald and Path

    CXXIX.CF559E Gerald and Path

    考虑将所有线段按照固定的那一端从小往大排序,并且对线段的端点离散化。

    这之后,设 \(f_{i,j}\) 表示当前处理到线段 \(i\),且所有线段中最右的那根的右端点不右于位置 \(j\)(即可以在 \(j\) 左面或与 \(j\) 重合)时的最优答案。

    我们考虑,假设我们放了一根线段 \([l,r]\)。因为不知道将来会放什么东西把它盖掉一部分,所以我们干脆在放线段 \([l,r]\) 时,同时也放下线段 \([l,l],[l,l+1],[l,l+2],\dots,[l,r-1]\),这样就不用担心被盖掉等讨论了。

    于是我们现在考虑处理第 \(i\) 根线段。设其向左是 \([l,p]\),向右是 \([p,r]\)

    首先,其有可能在接下来(或者在 \(i\) 之前就已经)被完全覆盖掉。于是一开始就有 \(f_{i-1,j}\rightarrow f_{i,j}\)

    其次,考虑其向右摆。向右摆,就意味着最右位置一定是 \(r\)——若最右位不是 \(r\),则一定在 \(i\) 之前还有一条向右摆的线段 \([p',r']\) 满足 \(r'\geq r\)。但是因为我们已经按照 \(p\) 递增排序了,故必有 \([p,r]\subset[p',r']\),即 \([p,r]\) 被完全覆盖,我们已经在上面说过了。

    则有 \(f_{i,r}\leftarrow f_{i-1,p}+[p,r]\)——因为我们已经令 \(f_{i,j}\) 表示“不右于”的最优值,所以就不用费尽心思枚举 \(i-1\) 时的最右位置了。同时,也不用担心重叠问题,因为按照我们上述讨论,为了避免重叠,我们直接将线段 \([l,r]\) 看作了所有 \([l,l\sim r]\)

    但是这也意味着,我们的线段 \([p,r]\) 也要被看作是所有的 \([p,p\sim r]\)。于是我们枚举 \(j\in[p,r]\),则有 \(f_{i,j}\leftarrow f_{i-1,p}+[p,j]\)

    然后就考虑其向左摆了。向左摆,就意味着最右位置不一定是 \(p\)——因为完全可以存在一条 \([p',r']\),满足 \(r'>p\operatorname{and}p'>l\),即两者无交。

    首先,最右位是 \(p\) 的就可以跟之前一样类似地转移,不在话下。

    然后,对于最右位不是 \(p\) 的,我们枚举一个 \(j<i\),表示最右位是 \(j\) 对应的 \([p',r']\)(明显这里上述 \(r'>p\operatorname{and}p'>l\) 应被满足)。则所有 \(k\in(j,i]\) 都应该向左摆,因为若其向右摆,要么右端点在 \(r'\) 左边或与 \(r'\) 重合,被完全包含;要么右端点在 \(r'\) 右边,则 \(r'\) 就不是最右位了。

    设所有 \(k\in(j,i]\) 的东西中,最左的那个是 \([l'',p'']\),则整个 \([j,i]\) 中所有线段,加一块可以变成一个 \([l'',r']\) 的大线段,且该线段是最右的。于是此处就可以跟前面一样,枚举一个 \(k\in[l'',r']\) 进行转移了。

    时间复杂度 \(O(n^3)\),因为枚举了 \(i,j,k\) 三个元素。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,f[110][310];
    pair<int,int>p[110];
    vector<int>v;
    int L[110],P[110],R[110],res;
    #define bs(x) lower_bound(v.begin(),v.end(),x)-v.begin()+1
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d%d",&p[i].first,&p[i].second),v.push_back(p[i].first),v.push_back(p[i].first-p[i].second),v.push_back(p[i].first+p[i].second);
    	sort(p+1,p+n+1),sort(v.begin(),v.end()),v.resize(m=unique(v.begin(),v.end())-v.begin());
    	for(int i=1;i<=n;i++)L[i]=bs(p[i].first-p[i].second),P[i]=bs(p[i].first),R[i]=bs(p[i].first+p[i].second);
    	for(int i=1;i<=n;i++){
    		memcpy(f[i],f[i-1],sizeof(f[i]));
    		for(int j=P[i];j<=R[i];j++)f[i][j]=max(f[i][j],f[i-1][P[i]]+v[j-1]-v[P[i]-1]);
    		for(int j=i;j;j--){
    			int Rmax=(j==i?P[j]:R[j]);
    			if(Rmax<P[i])continue;
    			int Lmin=(j==i?L[j]:P[j]);
    			for(int k=j+1;k<=i;k++)Lmin=min(Lmin,L[k]);
    			for(int k=Lmin;k<=Rmax;k++)f[i][k]=max(f[i][k],f[j-1][Lmin]+v[k-1]-v[Lmin-1]);
    		}
    		for(int j=1;j<=m;j++)f[i][j]=max(f[i][j],f[i][j-1]);
    	}
    	for(int i=1;i<=m;i++)res=max(res,f[n][i]);
    	printf("%d\n",res);
    	return 0;
    }
    

  • 相关阅读:
    IOS中的国际化的使用(Xcode 6.0之后的使用步骤)
    KVC,KVO的区别和使用
    通知,代理,block 单例的使用和区别
    NSoperation的使用
    多线程之Nsthread的使用方法
    多线程的之GCD的介绍
    IOS中生成证书、真机调试、上线发布程序的步骤
    IOS之NavigationController跳转到指点的界面
    IOS之截取字符串的使用方法
    ios 之定时器的使用的技巧(结合runloop)使用
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601493.html
Copyright © 2011-2022 走看看