SCAU 计算智能 三只DP

8633 回文划分

Description

    我们说一个字符串是回文串,那么意味着这个串从两边读起来的字母都是一样的。例如racecar是回文串,
然而fastcar则不是。
    对一个串的划分意思是将一个串划分为若干个部分。例如,racecar可以划分为race 和car两部分。给出
一个串,要把这个串划分为若干个回文串,那么至少要把这个串划分为多少部分?
例如
'racecar'已经是回文串,划分为1 个部分即可(这个部分就是racecar)。
'fastcar' 需要被划分为七个部分 ('f', 'a', 's', 't', 'c', 'a', 'r')。根据回文串的定义,单个字母也是回文串。
'aaadbccb' 分成可以被分为三个回文串 ('aaa', 'd', 'bccb')。找不到更少的划分方法。

 

输入格式

输入的第一行是数字T,表示输入文件含有T个CASE。之后有T行,每行有一个长度不大于1000的字
符串,全部由小写字母组成,中间没有空格。

输出格式

对于每个CASE,输出一个数字,表示对该字符串的回文串最小划分。

 

输入样例

3
racecar
fastcar
aaadbccb


 

输出样例

1
7
3

思路: 

先缩小规模考虑,假如是一个字符,那么他就分一次。

假如是两个,那么就有两种可能,第一,可以和第一个构成回文串,分一次,第二种不可以和前面构成回文串,分两次。

假如是三个或者更多字符,还是有两种可能,第一,最后一个字符可以和第一个字符构成回文串,分一次。

第二,不可以和第一个构成回文串,这时,最后一个字符有可能和前面某一段字符串构成回文串[st,end],st表示回文串开头字符,end表示回文串结尾字符。那么[st,end]分的次数,取决于前面已经分好的回文串个数再+1。

题目要求求取最小的次数,我们只需要遍历,end之前的所以字符st,分别组成回文字符串[st,end],比较取最小的次数即可。

到这里,就不难写出转移式:


dp[i]=1;(0~i)构成回文串;

dp[i]=min(dp[i],dp[j-1]+1);j取(1~i);

 

代码:

#include<iostream>
using namespace std;
#include<string>
string s;
int dp[1001];
bool check(int p1,int p2) {//检查是否回文串
	for (; p1 < p2; p1++, p2--) {
		if (s[p1] != s[p2]) { return false; }
	}
	return true;
}
int match(int end) {
	int num = dp[end - 1] + 1;
	for (int st = 0; st <= end - 1;st++){
		if (check(st+1, end))num=min(dp[st]+1,num);//依次比较求取所构成回文串[st+1,end]前面最小划分回文串的次数。
	}
	return num;
}
int main() {
	int T; cin >> T;
	dp[0] = 1;
	for (int i = 0; i < T;i++) {
		cin >> s;
		for (int i = 1; i < s.size();i++) {
			if (check(0, i))dp[i] = 1;//与第一个构成回文串
			else { dp[i] = match(i); }
		}
		cout << dp[s.size() - 1] << endl;
	}

}

 

8615 快乐

Description

Lian是一个喜欢看动画片的人,自从成为ACMer(ACM爱好者)之后,他又迷上了网上做题。做题让他快乐,不过这也是需要付出精力的!!
假设有n道题,Lian做出第i道题后,他可以获得的快乐指数将增加gethappy[i],而消耗掉的精力将是losspow[i]。
假设Lian初始的快乐指数为1,精力为2000。可以理解,如果他消耗完了所有的精力那他得到再多的快乐都没有用。
你的任务就是帮他计算他所能得到的最多的快乐指数,且最后他依然有多余的精力(即至少为1)。

 

输入格式

第一行输入一个整数n,表示有n道题。(n<=50)
第二行输入n个整数,表示gethappy[1]到gethappy[n]
第三行输入n个整数,表示losspow[1]到losspow[n]。

 

输出格式

一个整数,表示Lian所能获得的最大快乐指数。

 

输入样例

3
15 23 61
350 1301 1513


输出样例

77

 

思路:

典型的背包问题。


1.问题抽象化

2.建立模型

3.寻找约束条件

4.验证是否最优

5.寻找转移方程

6.求出解的组成


对于每一道题,我们有两种可能性:

1.当前精力不够,做不出来dp[i][j]=dp[i-1][j];

2.当前精力足够,就考虑做这道题题的收益大还是不做这道题收益大。

dp[i][j]=max(dp[i-1][j-loss[i]]+happy[i],dp[i-1][j]);

初始化,做0道题快乐值为0,精力为0时快乐值为0;

#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
int main() {
	int n; cin >> n;
	vector<vector<int>>dp(n + 1, vector<int>(2001, 0));
	vector<int>happy(n + 1, 0);
	vector<int>loss(n + 1, 0);
	for (int i = 1; i <= n;i++) {
		cin >> happy[i];
	}
	for (int i = 1; i <= n; i++) {
		cin >> loss[i];
	}
	for (int i = 1; i <= n;i++) {
		for (int j = 1; j <= 2000;j++) {
			if (j>loss[i]) {
				dp[i][j] = max(dp[i - 1][j],dp[i-1][j-loss[i]]+happy[i] );
			}
			else { dp[i][j] = dp[i - 1][j]; }
		
		}
	}
	cout << dp[n][2000] + 1 << endl;
}

 

 

 

18308 最长公共子序列

Description

给定两个字符串,请输出这两个字符串的最大公共子序列

 

输入格式

两行,一行一个字符串(不包括空格,Tab键),长度不超过1000

 

输出格式

输出最大公共子序列的长度

 

输入样例

abbca
aba

输出样例

3

 

思路:

其实思路无非是分治或者减治;

我们假设,对于一个序列前面i-1和另一个序列前面j-1的最大公共子序列已经求得,对于第i个字符,有两种可能性。

1.i与j相同,那么最大公共子序列为前面结果+1;dp[i][j]=dp[i-1][j-1]+1;

2.i与j不同,那么可能存在某个序列的尾部字符可能和另一个序列的某个字符相等使得最长公共子序列扩展,若存在,为dp[i-1][j]或者dp[i][j-1],这两个值都已经作为子问题被求解。

dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

 

代码:

#include<iostream>
using namespace std;
#include<algorithm>
#include<vector>
#include<string>
int main() {
	string s1, s2;
	cin >> s1 >> s2;
	vector<vector<int>>dp(s1.size()+1,vector<int>(s2.size()+1,0));
	for (int i = 1; i <=s1.size();i++) {
		for (int j = 1; j <= s2.size(); j++) {
			if (s1[i - 1] == s2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; }
			else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
		}
	}
	cout << dp[s1.size()][s2.size()] << endl;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值