ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOI2009] 诗人小G [题解]

2021-07-08 21:31:54  阅读:155  来源: 互联网

标签:arr ch int 题解 sum ld now 诗人 NOI2009


诗人小G

题目大意

给出 \(n\) 个长度不超过 \(30\) 的句子,要求你对其进行排版。

对于每一行,有一个规定的行标准长度 \(L\) ,每一行的不协调度等于该行的实际长度与行标准长度差的绝对值的 \(P\) 次方,而每一个排版的不协调度为所有行不协调度的总和。

每一行可以放入若干个句子,相邻的两个句子之间用空格隔开,每行的实际长度为该行拥有的字符数量(空格算一个字符)。

分析

首先我们比较容易想到 \(DP\) 。

设 \(f[i]\) 表示前 \(i\) 句诗排版的最小不协调度,很显然,状态转移方程为:

\[f[i]=min \{ f[j]+|sum[i]-sum[j]+(i-j-1)-L|^p \} (0\le j\le i) \]

很显然此时时间复杂度为 \(O(n^2)\) ,并不能解决该问题。

注意到我们可以设 \(val(i,j)=|sum[i]-sum[j]+(i-j-1)-L|^p\) ,因此我们可以证明 \(val(i,j)\) 满足四边形不等式,那我们的 \(DP\) 就会满足决策单调性,凭此我们能够达到优化该 \(DP\) 的目的。

下证 \(val(i,j)\) 满足四边形不等式:

当然,该证明并不太严格,严格证明需要讨论 \(x<-c,-c \le x\le 0,x>0\) 与 \(p\) 的奇偶性,加上求导即可证明。

于是,我们可以发现该 \(DP\) 具有决策单调性,因此可以直接使用前置知识中的方法求出最终的答案。

CODE

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long double ld;
const int N=1e5+10,INF=1e18;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w; 
}
struct node{
	int l,r,p; 
}arr[N]; //队列封装所有状态 
int T,n,L,P,st,nd;
int num[N];
char sten[N][31]; //由于要输出,记录下输入的句子 
int sum[N]; //记录句子长度的前缀和
ld f[N];
int g[N]; //g数组记录最佳决策点 
inline ld power(ld x,int k) //快速幂,大材小用 
{
	if(x<0) x=-x; //防止结果返回负数 
	ld res=1;
	while(k){
		if(k&1) res=res*x;
		x=x*x,k>>=1;
	}
	return res;
}
inline void in()
{
	char ch=getchar();
	for(register int i=1;i<=n;i++){
		int l=0;
		while((int)ch<33||(int)ch>127||ch=='-') ch=getchar();
		while((int)ch>=33&&(int)ch<=127&&ch!='-') sten[i][++l]=ch,ch=getchar();
		num[i]=l; //记录单个句子长度,方便输出 
		sum[i]=sum[i-1]+num[i]; //更新前缀和 
	}
}
inline int find(node now,int x)
{
	int ans=now.r;
	int l=now.l,r=now.r,v=now.p;
	while(l<=r){
		int mid=(l+r)/2;
		ld com1=f[x]+power(sum[mid]-sum[x]+(mid-x-1)-L,P);
		ld com2=f[v]+power(sum[mid]-sum[v]+(mid-v-1)-L,P);
		if(com1<=com2) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
inline void update(int x)
{
	int pos=-1;
	for(register int i=nd;i>=st;i--){
		node now=arr[i];
		ld temp1=f[x]+power(sum[now.l]-sum[x]+(now.l-x-1)-L,P);
		ld temp2=f[now.p]+power(sum[now.l]-sum[now.p]+(now.l-now.p-1)-L,P); //左区间端点 
		ld temp3=f[x]+power(sum[now.r]-sum[x]+(now.r-x-1)-L,P);
		ld temp4=f[now.p]+power(sum[now.r]-sum[now.p]+(now.r-now.p-1)-L,P); //右区间端点 
		if(temp1<=temp2) pos=now.l,nd--;
		else if(temp3>temp4) break;
		else { pos=find(now,x),arr[i].r=pos-1; break; }
	}
	if(pos==-1) return;
	arr[++nd].l=pos;
	arr[nd].r=n,arr[nd].p=x;
}
inline void out(int x,int last)
{
	if(x==0) return;
	out(g[x],x);
	for(register int i=g[x]+1;i<=x;i++){
		for(register int j=1;j<=num[i];j++) printf("%c",sten[i][j]);
		if(i<x) printf(" ");
	}
	puts("");
}
signed main()
{
	T=read();
	while(T--){
		n=read(),L=read(),P=read();
		in(); //读入
		st=nd=1; //队首队尾皆为1,
		arr[st].l=1,arr[st].r=n,arr[st].p=0;
		for(register int i=1;i<=n;i++){
			node now=arr[st]; //取出队头 
			f[i]=f[now.p]+power(sum[i]-sum[now.p]+(i-now.p-1)-L,P);
			g[i]=now.p; //记录当前点的最佳决策点 
			if(now.r>i) arr[st].l=i+1;
			else st++; //队头出队
			update(i); //更新队列 
		}
		if(f[n]>INF||f[n]<0) puts("Too hard to arrange");
		else{
			int ans=f[n];
			printf("%lld\n",ans);
			out(g[n],n);
			for(register int i=g[n]+1;i<=n;i++){
				for(register int j=1;j<=num[i];j++) printf("%c",sten[i][j]);
				if(i<n) printf(" ");
			}
			puts("");
		}
		if(T) puts("--------------------");
	}
	printf("--------------------");
	return 0;
}

标签:arr,ch,int,题解,sum,ld,now,诗人,NOI2009
来源: https://www.cnblogs.com/Defoliation-ldlh/p/14988235.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有