目录
132. 分割回文串 II
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
示例:
输入: "aab"
输出: 1
解释: 进行一次分割就可将 s 分割成 ["aa","b"] 这样两个回文子串。
分析: 定义状态dp[i]表示分割字符串str[0,i]最少需要分割多少次。
状态转移:j位于0~i之间,如果str[j,i]是回文串,dp[i]就是dp[j-1] + 1, 因为对于s[j,i]已经是回文串了,所以做一次单独的分割,剩下的就是dp[j-1]。
可以用个标记数组mp[i][j]表示从i到j的字符串是回文串。
class Solution {
public:
int minCut(string s) {
int n = s.size();
if(n<2)return 0;
vector<int>dp(n,n);
vector<vector<bool> > mp( n, vector<bool>(n,false) );
dp[0] = 0;
mp[0][0] = true;
for(int i = 1;i < n;++i){
for(int j = 0;j<=i;++j){
if(s[i] == s[j] && ( i-j<2 || mp[j+1][i-1] ) ) {
dp[i] = min(dp[i],!j?0:dp[j-1]+1);
mp[j][i] = true;
}
}
}
return dp[n-1];
}
};
139. 单词拆分
dp[i]:表示字符串s[0,i]可以被拆。然后他的状态对前面的暴力哈希判断就可以了。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
vector< bool> dp(n+1,false);
unordered_set<string> hs(wordDict.begin(),wordDict.end());
dp[0] = true;
for(int i = 1;i<=n;++i){
for(int j = 0;j<i;++j)
{
if(dp[j] && hs.find( s.substr(j,i-j) )!= hs.end())
{
dp[i] = true;break;
}
}
}
return dp[n];
}
};
140. 单词拆分 II
相比于上一题,打一下标记,找一下路径。
class Solution {
public:
vector<string> path,res;
vector<string> wordBreak(string s, vector<string>& wordDict) {
int n = s.size();
unordered_set<string> hs(wordDict.begin(),wordDict.end());
vector<bool> dp(n+1,false);
vector< vector<bool> > mp(n+1, vector<bool> (n+1,false));
dp[0] = true;
for(int i = 1;i<=n;++i)
for(int j = 0;j<i;++j){
if(dp[j] && hs.find( s.substr(j,i-j) ) != hs.end() ){
dp[i] = true;
mp[j][i] = true;
}
}
getpath(s,mp,n,n);
return res;
}
void getpath(string &s,vector< vector<bool> > &mp,int len,int cur){
if(cur == 0){
string tmp;
for(auto it = path.rbegin();it!=path.rend();++it){
tmp += *it;
tmp += " ";
}
tmp.erase(tmp.end()-1);
res.push_back(tmp);
return;
}
for(int i = 0;i<len;++i){
if(mp[i][cur]){
path.push_back( s.substr(i,cur-i) );
getpath(s,mp,len,i);
path.pop_back();
}
}
}
};
152. 乘积最大子序列
给定一个整数数组 nums
,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
分析:
当前数>0时,直接最大值就是*当期前数,否则用最小值*当前数。
class Solution {
public:
int maxProduct(vector<int>& nums) {
int ans = INT_MIN;
int n = nums.size();
vector<int> _max(n+1,INT_MIN),_min(n+1,INT_MAX);
_max[0] = _min[0] = ans = nums[0];
if(n <= 1)return nums[0];
for(int i = 1;i<n;++i){
if(nums[i] > 0){
_max[i] = _min[i] = nums[i];
_max[i] = max(_max[i],_max[i-1]*nums[i]);
_min[i] = min(_min[i],_min[i-1]*nums[i]);
}
else{
_max[i] = _min[i] = nums[i];
_max[i] = max(_max[i],_min[i-1] * nums[i]);
_min[i] = min(_min[i],_max[i-1] * nums[i]);
}
ans = max(ans,_max[i]);
}
return ans;
}
};
198. 打家劫舍
简单的动态规划:
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n,0);
if(n==0)return 0;
if(n==1)return nums[0];
dp[0] = nums[0],dp[1] = max(nums[0],nums[1]);
for(int i = 2;i<n;++i){
dp[i] = max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[n-1];
}
};
213. 打家劫舍 II
讨论下选0不选0号即可:
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(!n)return 0;
if(n==1)return nums[0];
if(n==2)return max(nums[0],nums[1]);
if(n==3)return max(max(nums[0],nums[1]),nums[2]);
vector< vector<int> > dp(2,vector<int>(n,0) );
dp[0][0] = nums[0];
dp[0][1] = max(nums[0],nums[1]);
dp[0][2] = max(dp[0][1],dp[0][0] + nums[2]);
dp[1][1] = nums[1];
dp[1][2] = max(nums[1], nums[2]);
for(int i = 3;i<n;++i){
if(i != n-1)
dp[0][i] = max(dp[0][i-1],dp[0][i-2]+nums[i]),
dp[1][i] = max(dp[1][i-1],dp[1][i-2]+nums[i]);
else dp[0][i] = max(dp[0][i-1],dp[1][i-2] + nums[i]);
}
return dp[0][n-1];
}
};
这两道题后面还有一道这样经典的题:如果限制可偷的数量,怎么写?
BZOJ 2151
Description
A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n。并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度。但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置。值得注意的是1号和n号也算相邻位置!)。最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将m棵树苗全部种上,给出无解信息。
Input
输入的第一行包含两个正整数n、m。第二行n个整数Ai。
Output
输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。
Sample Input
【样例输入1】
7 3
1 2 3 4 5 6 7
【样例输入2】
7 4
1 2 3 4 5 6 7
Sample Output
【样例输出1】
15
【样例输出2】
Error!
分析:我们对于这种不相邻问题有一种这样的处理办法:
用优先队列 + 链表的形式,选择队首元素Xi,然后把它相邻两个元素Xi-1 Xi+1取出, 然后两者相加,再减掉Xi后的值,再添加进去。如果下次取这个值的话,显然就是没取Xi,而是取了它相邻的元素。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define oo cout<<"!!!"<<endl;
typedef long long ll;
typedef unsigned long long ull;
#define ms(s) memset(s, 0, sizeof(s))
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
//head
const int maxn = 1e6+11;
int nxt[maxn],pre[maxn],a[maxn];
bool vis[maxn];
int main()
{
priority_queue<pair<int,int> >q;
int n,m;cin>>n>>m;
for(int i = 1;i<=n;++i)scanf("%d",a+i),q.push(make_pair(a[i],i));
for(int i = 1;i<n;++i)nxt[i] = i+1;nxt[n] = 1;
for(int i = 2;i<=n;++i)pre[i] = i-1;pre[1] = n;
if(m > n/2)return puts("Error!")&&1;
ll ans = 0;
while(m--){
pair<int,int> tmp = q.top();q.pop();
int x = tmp.second;
while(vis[x]){tmp = q.top();x = tmp.second;q.pop();}
int v = tmp.first;
ans+=v;
a[x] = a[pre[x]] + a[nxt[x]] - a[x];
vis[pre[x]] = vis[nxt[x]] = true;
pre[x] = pre[pre[x]];
nxt[x] = nxt[nxt[x]];
pre[ nxt[x] ] = x;
nxt[ pre[x] ] = x;
q.push(make_pair(a[x],x));
}
cout << ans <<endl;
return 0;
}
类似地一道题:BZOJ-1150
题意:给一条直线的坐标,每两个点可以用一根线连接,每个点只能连一根线,只能选择K个线,求线的总长度最短。
分析:连接相邻的,处理出来每两个相邻的距离,然后选择不相交最小,和上题类似:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define oo cout<<"!!!"<<endl;
typedef long long ll;
typedef unsigned long long ull;
#define ms(s) memset(s, 0, sizeof(s))
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
//head
const int maxn = 1e6+11;
int nxt[maxn],pre[maxn],a[maxn];
bool vis[maxn];
int main()
{
int n,k;cin>>n>>k;
int last = 0;
for(int i = 1;i<=n;++i){
int x;cin>>x;
a[i] = x - last;last = x;
pre[i] = i-1,nxt[i] = i+1;
}
pre[2] = 0;nxt[n]=0;
priority_queue< pair<int,int>,vector<pair <int,int> >, greater<pair<int,int> > > q;
for(int i = 2;i<=n;++i) q.push(make_pair(a[i],i));
int ans = 0;
a[0] = inf;
while(k--){
pair<int,int> tmp = q.top();q.pop();
int x = tmp.second;
while(vis[x]){tmp = q.top();x = tmp.second;q.pop();}
int v = tmp.first;
//cout << v << endl;
ans += v;
a[x] = a[pre[x]] +a[nxt[x]] - a[x];
vis[pre[x]] = vis[nxt[x]] = true;
pre[x] = pre[pre[x]];
nxt[x] = nxt[nxt[x]];
pre[nxt[x]] = x;
nxt[pre[x]] = x;
q.push(make_pair(a[x],x));
}
cout << ans <<endl;
return 0;
}