【洛谷P1415】拆分数列【dp】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P1415
给出一列数字,需要你添加任意多个逗号将其拆成若干个严格递增的数。如果有多组解,则输出使得最后一个数最小的同时,字典序最大的解(即先要满足最后一个数最小;如果有多组解,则使得第一个数尽量大;如果仍有多组解,则使得第二个数尽量大,依次类推……)。


思路:

由于要先把最后一位尽量小,考虑先求出最后一位的最小答案。
f [ i ] f[i] f[i]表示在满足前 i i i位的划分单调递增时,以第 i i i位为末,且这一次的划分的值尽量小的时候划分的首位。
简单来说,就是找到一个 j j j满足在 j j j开始划分可以满足单调递增且 j ∼ i ‾ \overline{j\sim i} ji尽量小的方案数。
显然只需要让 j j j尽量大就可以了。就倒序枚举 j j j,如果 f [ j − 1 ] ∼ j − 1 ‾ &lt; j ∼ i ‾ \overline{f[j-1]\sim j-1}&lt;\overline{j\sim i} f[j1]j1<ji就让 f [ i ] = j f[i]=j f[i]=j
其中 x ∼ y ‾ \overline{x\sim y} xy 可以预处理搞定。
接下来求出满足前面尽量大的答案。
g [ i ] g[i] g[i]表示在满足后 i i i位的划分满足单调递增时,以第 i i i位为首,且这一次的划分的值尽量大的时候划分的末位。
显然初始化 d [ f [ n ] ] = n d[f[n]]=n d[f[n]]=n
推方程思路和 f f f基本一样。
注意处理前导0。可以用 s u m [ x ] [ y ] sum[x][y] sum[x][y]表示 x ∼ y ‾ \overline{x\sim y} xy的前导0个数。
这样的时间复杂度就是 O ( n 3 ) O(n^3) O(n3)的。实际上跑起来会小得多。


代码:

#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

const int N=510;
int n,a[N],f[N],g[N],sum[N][N];
string t[N][N];

bool check(int S1,int T1,int S2,int T2)
{
	if (T1-S1-sum[S1][T1]<T2-S2-sum[S2][T2]) return 1;
	if (T1-S1-sum[S1][T1]>T2-S2-sum[S2][T2]) return 0;
	string s1=t[S1+sum[S1][T1]][T1],s2=t[S2+sum[S2][T2]][T2];
	for (int i=0;i<min(s1.size(),s2.size());i++)
	{
		if (s1[i]<s2[i]) return 1;
		if (s1[i]>s2[i]) return 0;
	}
	return 0;
}

void dp1()
{
	f[1]=1;
	for (int i=2;i<=n;i++)
		for (int j=i;j>=1;j--)
			if (check(f[j-1],j-1,j,i))
			{
				f[i]=j;
				break;
			}
}

void dp2()
{
	g[f[n]]=n;
	int k=0;
	for (int i=f[n]-1;!a[i]&&i;i--) g[i]=n,k++;
	for (int i=f[n]-1-k;i>=1;i--)
		for (int j=f[n]-1;j>=i;j--)
			if (check(i,j,j+1,g[j+1]))
			{
				g[i]=j;
				break;
			}
	int i;
}

int main()
{
	while (scanf("%1d",&a[++n])==1)
		for (int i=1;i<=n;i++)
		{
			t[i][n]=t[i][n-1]+(char)(a[n]+48);
			if (sum[i][n-1]==max(n-i,0)&&!a[n]) sum[i][n]=sum[i][n-1]+1;
				else sum[i][n]=sum[i][n-1];
		}
	n--;
	dp1();
	dp2();
	int j=g[1];
	for (int i=1;i<=n;i++)
	{
		printf("%d",a[i]);
		if (i==j&&i!=n)
		{
			j=g[i+1];
			putchar(',');
		}
	}
	return 0;
}

这道题思路还是不难,但是敲了我 3 h 3h 3h。。。恐怖如斯
话说这道题有要求 O ( n log ⁡ n ) O(n\log n) O(nlogn)的加强版 → \to P2282[HNOI2003]历史年份,好像是用 h a s h + hash+ hash+二分判断两个子串的大小,线段树维护f的最大值。。。orz

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值