ural Mnemonics and Palindromes (dp)

http://acm.timus.ru/problem.aspx?space=1&num=1635

给出一个字符串,将这个字符串分成尽量少的回文串。

起初没有思路,想着应该先预处理出所有的回文串,然后进行dp。但是字符串的长度是4000,O(n^3)肯定不行,其实可以转化为O(n^2),就是枚举中点而不是枚举起点和终点,又NC了吧。

然后就是线性的dp了。dp[i]表示到第i位为止最少的回文串数,那么dp[i] = min(dp[i],dp[j+1]+1),j < i 且i到j也是回文串。

输出路径时用pre数组记录每个得到的回文串的起始位置就行。

#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
#define LL __int64
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 4010;

char s[maxn];
int tmp[maxn][maxn];
int len;
int dp[maxn],pre[maxn];
int is_p[maxn];

void init()
{
	//枚举中点,求出所有回文串
	memset(tmp,0,sizeof(tmp));
	for(int i = 0; i < len; i++)
	{
		tmp[i][i] = 1;
		for(int j = 1;; j++) //长度为奇数的回文串
		{
			if(i-j < 0 || i+j >= len)
				break;
			if(s[i-j] == s[i+j])
				tmp[i-j][i+j] = 1;
			else break;
		}
		for(int j = 1; ; j++)//长度为偶数的回文串
		{
			if(i-j+1 < 0 || i+j >= len)
				break;
			if(s[i-j+1] == s[i+j])
				tmp[i-j+1][i+j] = 1;
			else break;
		}
	}
}

int main()
{
	while(~scanf("%s",s))
	{
		len = strlen(s);
		init();
		memset(dp,INF,sizeof(dp));
		memset(pre,-1,sizeof(pre));
		for(int i = 0; i < len; i++)
		{
			if(tmp[0][i] == 1)
			{
				dp[i] = 1;
				pre[i] = 0;
			}
		}
		for(int i = 0; i < len; i++)
		{
			if(dp[i] == 1) continue;
			for(int j = 0; j < i; j++)
			{
				if(tmp[j+1][i] && dp[i] > dp[j]+1)
				{
					dp[i] = dp[j]+1;
					pre[i] = j+1;
				}
			}
		}
		printf("%d\n",dp[len-1]);
		memset(is_p,-1,sizeof(is_p));
		int t = len-1,tt;
		while(1)
		{
			if(t < 0)
				break;
			tt = pre[t];
			is_p[tt] = 1;
			t = tt-1;
		}
		for(int i = 0; i < len; i++)
		{
			if(is_p[i] == 1 && i != 0)
				printf(" ");
			printf("%c",s[i]);
		}
		printf("\n");
	}
	return 0;
}

时间: 2024-10-12 22:30:30

ural Mnemonics and Palindromes (dp)的相关文章

Ural 1635 Mnemonics and Palindromes(DP)

题目地址:Ural 1635 又是输出路径的DP...连着做了好多个了.. 状态转移还是挺简单的.要先预处理出来所有的回文串,tag[i][j]为1表示字符串i--j是一个回文串,否则不是回文串.预处理时要用n^2的方法,即枚举回文串中间,可以分奇数和偶数两种分别求一次. 然后dp转移方程为,若tag[j][i]==1,dp[i]=min(dp[i],dp[j-1]+1); 对于最令人讨厌的路径输出,可以用pre来记录回文串前驱分裂点,然后根据这些分裂点来输出. 代码如下: #include <

URAL 1167. Bicolored Horses (DP)

题目链接 题意 :农夫每天都会放马出去,然后晚上把马赶入马厩,于是让马排成一行入马厩,但是不想马走更多的路,所以让前p1匹入第一个马厩,p2匹马入第二个马厩…………但是他不想让他的任何一个马厩空着,所有的马都必须入马厩.有两种颜色的马,如果 i 匹黑马与 j 匹白马同在一个马厩,不愉快系数是 i * j,总系数就是k个系数相加.让总系数最小. 思路 : dp[i][j] 代表的是前 i 个马厩放 j 匹马的最小不愉快系数值. 1 //1167 2 #include <cstdio> 3 #in

URAL 1073 Square Country(DP)

题目链接 题意 :这个人要投资地,每块地都是正方形并且边长都是整数,他希望他要买的地尽量的少碎块.每买一块地要付的钱是边长的平方,而且会得到一个一份证书,给你一个钱数,让你求出能得到的证书个数. 思路 :其实就是求x12+x22+--+Xn2中的最小的n. 1 //1073 2 #include <stdio.h> 3 #include <iostream> 4 #include <math.h> 5 6 using namespace std ; 7 8 int a[

Ural 1146 Maximum Sum(DP)

题目地址:Ural 1146 这题是求最大子矩阵和.方法是将二维转化一维. 首先用n*n的方法来确定矩阵的列.需要先进行预处理,只对每行来说,转化成一维的前缀和,这样对列的确定只需要前后两个指针来确定,只需要用前缀和相减即可得到.前后两个指针用n*n的枚举. 确定好了哪几列,那么再确定行的时候就转化成了一维的最大连续子序列的和.再来一次O(n)的枚举就可以. 这样,总复杂就变成了O(n^3),对于n为100来说,已经足够了. 代码如下: #include <iostream> #include

Ural 2018The Debut Album(DP)

题目地址:Ural 2018 简单DP.用滚动数组. 代码例如以下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #include <map> #i

Ural 1586 Threeprime Numbers(DP)

题目地址:Ural 1586 先定义一个prime三维数组来记录素数,若i*100+j*10+k为素数,则标记prime[i][j][k]为1,否则为0.这样对后面的处理很方便. 然后定义一个dp三维数组,dp[n][i][j]表示当前n位的十位数字为i,个位数字为j时的素数个数,这时候状态要从prime[k][i][j]为素数时转移过来,所以状态转移方程为: if(prime[j][k][h])         dp[i][k][h]+=dp[i-1][j][k] 代码如下: #include

Ural 1073 Square Country (DP)

题目地址:Ural 1073 DP水题.也可以说是背包. #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctype.h> #include <queue> #include <map> #include

【uva-11584】Partitioning by Palindromes(dp)

粗略的复杂度是L^3,长度最大是1000,,没敢做,之后发现其实这个复杂度的系数也不大,可以过,而且很快. dp[j] = dp[i - 1] + 1 (if(str[i] ~ str[j]为回文) 14327451 11584 Partitioning by Palindromes Accepted C++ 0.052 2014-10-09 09:33:17 #include<set> #include<map> #include<stack> #include<

uva 11584 Partitioning by Palindromes(dp)

题目链接 题意:给定一个字符串,分解成多个子串,每个子串都是回文串,问最少能分成多少个子串. 题解: dp[i]表示前i个字符串分割成最少回文子串的数量: 0<=j<=i;如果字符串从j到i是回文串,那么dp[i]=min(dp[i],dp[j-1]+1); #include <iostream> using namespace std; int dp[1005]; string s; bool ok(int j,int i) { while(j<=i) { if(s[j]!