HDU 5009 Paint Pearls DP 双向链表

版权声明: https://blog.csdn.net/nwpu2017300135/article/details/81637678

http://acm.hdu.edu.cn/showproblem.php?pid=5009

题意:对一段字符串操作,一次操作可以操作一段连续的区间,开销为这段区间颜色种类数的平方。求使字符串每一个元素都被操作过至少一次的最小开销。

题解:DP的转移过程挺好想,就是dp[i]=max(dp[j]+num[j,i]^{2})(num(j,i)表示区间[i,j]的颜色种类数)但是朴素的写会超时。

算一下样例二会发现中间一大段只有3,2,4三种颜色,这一大段就可以直接跳过了,因为只要一直是这种三种颜色,肯定先搞前面的更优。这个跳过的过程可以用双向链表实现。这里用类似并查集的方式实现双向链表。

代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define maxn 50005
using namespace std;
int n;
int a[maxn],b[maxn],dp[maxn];
int pre[maxn],nxt[maxn];
int main()
{

	int i,j,k;
	int m;
	map<int,int > mp;
	pre[0]=-1;
	while(~scanf("%d",&n)){
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		mp.clear();
		memset(dp,0,sizeof(dp));
		m=0;
		a[0]=-1;
		//缩点,同色连续相当于一个点
		for(i=1;i<=n;i++){
			if(a[i]!=a[i-1])b[++m]=a[i];
		}
		//初始化链表
		for(i=1;i<=m;i++){
			pre[i]=i-1;
			nxt[i]=i+1;
		}
		for(i=1;i<=m;i++){
			if(mp[b[i]]==0)mp[b[i]]=i;//没有出现过的颜色
			else {
				k=mp[b[i]];
				nxt[pre[k]]=nxt[k];
				pre[nxt[k]]=pre[k];
				mp[b[i]]=i;
			}//dp的时候从后向前遍历,出现过的颜色直接跳过
			k=1;
			dp[i]=i;
			for(j=i-1;j!=-1;j=pre[j]){
				if(k*k>m)break;//如果大于说明还不如直接一个点一个点的操作
				int tmp=dp[j]+k*k;
				if(tmp>m)break;
				dp[i]=min(dp[i],tmp);
				k++;
			}
		}
		printf("%d\n",dp[m]);

	}
    //cout << "Hello world!" << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/nwpu2017300135/article/details/81637678