1. 程式人生 > >[NOI2009]詩人小G(決策單調性優化dp)

[NOI2009]詩人小G(決策單調性優化dp)

【題解】

經典的1D1D動態規劃優化 

狀態轉移方程:f[i]=min{ f[j]+abs(s[i]-s[j]+i-j-1-l)^p } 

決策單調性及證明:
http://blog.csdn.net/jasonzhu8/article/details/5928552

因此,對於每個數,其能作為最優決策的區間一定是連續的一段,
對於新求出的f[i],從後往前在i-1,i-2,…對應的決策區間內二分查詢 i的決策區間
用一個棧去維護每個數對應的決策區間即可 

此題f值巨大,需先用double計算,判斷答案是否小於10^18,如果是,在用long long算一遍(double精度不夠)

【程式碼】

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define INF 1000000000000000000ll
typedef long long LL;
char poem[100005][35];
double s[100005],f[100005];
int sta[100005],L[100005],R[100005],id[100005],from[100005];
int n,p,l,ps;
int max(int a,int b)
{
	if(a>b) return a;
	return b;
}
double jdz(double x)
{
	if(x<0) x=-x;
	return x;
}
LL jdz_L(LL x)
{
	if(x<0) x=-x;
	return x;
}
double W(int j,int i)
{
	double a=jdz(s[i]-s[j]+i-j-1-(double)l),ans=1;
	int k;
	for(k=1;k<=p;k++)
		ans*=a;
	return ans;
}
LL W_L(int j,int i)
{
	LL a=jdz_L((LL)s[i]-(LL)s[j]+(LL)i-(LL)j-1-(LL)l),ans=1;
	int k;
	for(k=1;k<=p;k++)
		ans*=a;
	return ans;
}
void jiaru(int k)
{
	int left,mid,right,i;
	for(;ps>0;ps--)
	{
		left=max(L[ps],k+1);
		if(f[k]+W(k,left)<=f[sta[ps]]+W(sta[ps],left))
		{
			if(L[ps]<=k+1)
			{
				if(L[ps]==k+1) ps--;
				else R[ps]=k;
				sta[++ps]=k;
				L[ps]=k+1;
				R[ps]=n;
				id[k+1]=k;
				return;
			}
		}
		else
		{
			right=R[ps];
			while(left<right)//左閉右開區間 
			{
				mid=(left+right+1)/2;
				if(f[k]+W(k,mid)<=f[sta[ps]]+W(sta[ps],mid)) right=mid-1;
				else left=mid;
			}
			if(left==n) return;
			R[ps]=left;
			sta[++ps]=k;
			L[ps]=left+1;
			R[ps]=n;
			id[left+1]=k;
			return;
		}
	}
}
LL get(int k)
{
	if(k==0) return 0;
	return get(from[k])+W_L(from[k],k);
}
void print(int k)
{
	int i;
	if(k==0) return;
	print(from[k]);
	for(i=from[k]+1;i<=k;i++)
	{
		printf("%s",poem[i]);
		if(i<k) printf(" ");
	}
	printf("\n");
}
int main()
{
	int T,i,j;
	scanf("%d",&T);
	for(;T>0;T--)
	{
		memset(poem,0,sizeof(poem));
		memset(s,0,sizeof(s));
		memset(id,0,sizeof(id));
		memset(from,0,sizeof(from));
		scanf("%d%d%d",&n,&l,&p);
		for(i=1;i<=n;i++)
		{
			scanf("%s",poem[i]);
			s[i]=(double)strlen(poem[i])+s[i-1];
		}
		sta[ps=1]=0;
		L[ps]=1;
		R[ps]=n;
		j=0;
		for(i=1;i<=n;i++)
		{
			if(j<id[i]) j=id[i];
			f[i]=f[j]+W(j,i);
			from[i]=j;
			jiaru(i);
		}
		if(f[n]>INF) printf("Too hard to arrange\n");
		else
		{
			printf("%lld\n",get(from[n])+W_L(from[n],n));
			print(n);
		}
		printf("--------------------\n");
	}
	return 0;
}