seq(2018.10.24)

一道(dp)题。。。
期望(40)分解法
预处理:离散化,然后让连续一段值相同的元素合并为一个元素。
正式(DP)
显然有个最差策略为每个元素处都切一次,则切的次数为元素的个数(-1)
相对地来说就是假设全部元素之间就已经切开,要尽量多地合并元素
(DP)的第一维用来确认当前是合并了值为多少的两个数值段,DP的第二维来记住最后一次合并是合并了哪个位置的两个线段
(DP[i][j]=)对于值为(1)(i+1)的数值段, 最后一次合并为合并(a[j])(a[j+1])这两个元素,最多能合并的总次数
而相对应的转移方程就是:
(DP[i][j] =max( DP[i-1][j']+1) (合并 a[j'] , a[j'+1] 不会与 合并a[j],a[j+1]冲突))
冲突是指合并(a[i],a[i+1])的同时也合并(a[j],a[j+1])会导致无法拼接成单调不降的序列,其充要条件是(i+1=j)且值为(a[i+1])的数值段在原序列中出现了不止(1)次,

空间和时间复杂度都是(O(n^2)),期望得分(40)

期望(100)分解法
优化:
(1.)滚动数组优化空间为(O(n))
(2.)因为对于每个(i=x),转移的时候只用考虑最大值和次大值,如果最大值和当前状态冲突,则用次大值更新

空间和时间复杂度都是(O(n)),期望得分(100)

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
int n,a[100001],cnt,b[100001],tot,pre[100001],nxt[100001],h[100001],deg[100001];
pair<int,int>dp[2],DP[2];
map<int,int>mp;
void add(int x,int y){pre[++cnt]=y;nxt[cnt]=h[x];h[x]=cnt;deg[x]++;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	for(int i=1;i<=n;i++)if(!mp[b[i]])mp[b[i]]=++tot;
	for(int i=1;i<=n;i++)a[i]=mp[a[i]];tot=0;
	for(int i=1;i<=n;i++)if(a[i]!=a[i+1])a[++tot]=a[i];
	for(int i=1;i<=tot;i++)add(a[i],i);
	for(int i=h[1];i;i=nxt[i])
		if(a[pre[i]+1]==2)
		{
			dp[1]=max(dp[1],make_pair(1,pre[i]));
			if(dp[1]>DP[1])swap(dp[1],DP[1]);
		}
	for(int i=2;i<cnt;i++)
	{
		dp[i&1]=dp[(i&1)^1];DP[i&1]=DP[(i&1)^1];
		for(int j=h[i];j;j=nxt[j])
			if(a[pre[j]+1]==a[pre[j]]+1)
			{
				if(DP[(i&1)^1].second+1!=pre[j]||deg[i]==1)dp[i&1]=max(dp[i&1],make_pair(DP[(i&1)^1].first+1,pre[j]));
				else dp[i&1]=max(dp[i&1],make_pair(dp[(i&1)^1].first+1,pre[j]));
				if(dp[i&1]>DP[i&1])swap(dp[i&1],DP[i&1]);
			}
	}
	printf("%d
",tot-1-DP[(cnt-1)&1].first);
}
原文地址:https://www.cnblogs.com/lcxer/p/9846216.html