1007.最大子序列和 **
DP集合划分:由于f[i]表示以i为右端点的所有区间集合,该集合可以分为两类,即只有i一个点,以及f[i-1]和i
如何确定答案的左右端点?
- 此过程可在DP过程中完成
- 考虑DP的转移过程,每次我们只在
f[i-1]+w[i]和w[i]中取最大值,只要f[i-1]<0,那么f[i]就取w[i],否则进行累和
所以每次更新成w[i]都是对之前区间的截断,也就是说我们找到的区间之间不相交
- 那么只要在每次截断时保存该区间左端点,并在
res
需要更新时更新左右端点即可
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+10;
int f[N],num[N];
int n;
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>num[i];
memset(f,-0x3f,sizeof f);
int l,r,res=-1;
for(int i=1,start ;i<=n;i++){
if(f[i-1]<0){ //前区间使得和变小,截断
f[i]=num[i];
start=i; //保存当前区间起始位置
}
else f[i]=f[i-1]+num[i];
if(f[i]>res){ //res变大,更新起始端点和结尾端点
res=f[i];
l=start,r=i;
}
}
if(res<0)res=0,l=1,r=n;
cout<<res<<" "<<num[l]<<" "<<num[r];
}
1045.最佳彩色带
集合的划分
:
不包含i和j
此时从f[i-1,j-1]转移
包含i但不包含j
此时从f[i,j-1]转移
(注意f[i,j-1]涵盖了包含i但不包含j的情况)包含j但不包含i
此时从f[i-1,j]转移
同时包含i和j
,由于i可以匹配多次,所以i不仅可以匹配j,也可匹配1~j-1中的元素,此时从f[i,j-1]+1转移
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=210,M=1e4+10;
int n,m,l;
int f[N][M];
int p[N],s[M];
int main(){
cin>>n;
cin>>m;
for(int i=1;i<=m;i++)cin>>p[i];
cin>>l;
for(int i=1;i<=l;i++)cin>>s[i];
f[0][0]=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=l;j++){
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(p[i]==s[j])f[i][j]=max(f[i][j],f[i][j-1]+1);
}
cout<<f[m][l];
}
1068.找更多硬币
- 01背包问题的变形
- 由于本题需要对状态进行回推,并且要求按照字典序
由小到大输出
选择方案
所以先从大到小
排序,以便再倒推的时候做出最小选择
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+10,M=110;
int n,m;
bool f[N][M];
int num[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>num[i];
sort(num+1,num+n+1,greater<int>());
f[0][0]=true;
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=num[i])f[i][j] |=f[i-1][j-num[i]];
}
if(!f[n][m])cout<<"No Solution";
else {
int i=n,j=m;
bool is_first=true;
while(i){
if(j>=num[i] && f[i-1][j-num[i]]){
if(is_first)is_first=false;
else cout<<" ";
cout<<num[i];
j-=num[i];
}
i--;
}
}
}
1093.PAT计数
- 状态机模型
- 当字符相等
s[i]==p[j]
时,表示可以从上一个状态转移,统计一共可以转移到状态3
的路径数量
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10,mod=1e9+7;
char s[N],p[]=" PAT";
int f[N][4];
int main(){
cin>>s+1;
int n=strlen(s+1);
f[0][0]=1; //起始状态路径为1
for(int i=1;i<=n;i++)
for(int j=0;j<=3;j++){
f[i][j]=f[i-1][j];
if(s[i]==p[j])f[i][j]=(f[i][j]+f[i-1][j-1])%mod;
}
cout<<f[n][3];
}
1101.快速排序
- 预处理出左边数的最大值以及右边数的最小值
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10;
int L[N],R[N]; //左边数的最大值,右边数的最小值
int a[N],n;
int main(){
cin>>n;
vector<int> res;
for(int i=1;i<=n;i++){
cin>>a[i];
L[i]=max(L[i-1],a[i]);
}
R[n+1]=0x3f3f3f3f;
for(int i=n;i>=1;i--){
R[i]=min(R[i+1],a[i]);
}
for(int i=1;i<=n;i++){
if(a[i]>L[i-1] && a[i]<R[i+1])res.push_back(a[i]);
}
if(!res.size())cout<<0<<endl<<endl;
else {
cout<<res.size()<<endl;
cout<<res[0];
for(int i=1;i<res.size();i++)cout<<" "<<res[i];
}
}