今日学习了状态机模型,明确他和状态压缩是两种不同的状态表示方式
islower(),isupper()函数用来判断字符是否为小写的大写字符
tolower(),toupper()来转换大小写
to_string()将数字转换为字符
明确了字符串为子序列元素的最长上升子序列写法,以及存最优路径的最长上升子序列
AcWing 1049. 大盗阿福
阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。
这条街上一共有 N 家店铺,每家店中都有一些现金。
阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。
作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。
他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?
输入格式
输入的第一行是一个整数 T,表示一共有 T 组数据。
接下来的每组数据,第一行是一个整数 N ,表示一共有 N 家店铺。
第二行是 N 个被空格分开的正整数,表示每一家店铺中的现金数量。
每家店铺中的现金数量均不超过1000。
输出格式
对于每组数据,输出一行。
该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。
数据范围
1≤T≤50,
1≤N≤1e5
输入样例:
2
3
1 8 2
4
10 7 6 14
输出样例:
8
24
样例解释
对于第一组样例,阿福选择第2家店铺行窃,获得的现金数量为8。
对于第二组样例,阿福选择第1和4家店铺行窃,获得的现金数量为10+14=24。
通过本题我们发现,当前店铺抢不抢和前一家抢没抢有关,且状态划分比较复杂
我们可以把上一家店铺是否抢的状态都保存下来,来判断
#include <iostream>
#include <algorithm>
using namespace std;
const int N=110,M=1e6;
int w[N],f[N][2];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
int n;cin>>n;
memset(f,0,sizeof f);
f[0][0]=0,f[0][1]=-M;//不能取起始点0点所以初始化负无穷
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++){
f[i][0]=max(f[i-1][0],f[i-1][1]);//上一个要么选,不选
f[i][1]=f[i-1][0]+w[i];
}
cout<<max(f[n][1],f[n][0])<<'\n';
}
return 0;
}
Acwing 1057. 股票买卖 IV
给定一个长度为 N 的数组,数组中的第 i个数字表示一个给定股票在第 i天的价格。
设计一个算法来计算你所能获取的最大利润,你最多可以完成 k𝑘 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。
输入格式
第一行包含整数 N 和 k,表示数组的长度以及你可以完成的最大交易笔数。
第二行包含 N 个不超过 10000 的非负整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤1e5
1≤k≤100
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1e5+7;
int w[N],f[N][110][2];//第i天,已经进行j次交易,状态为k(手上有无这个股票),获得的最大利润
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++) cin>>w[i];
memset(f,-0x3f,sizeof f);//所以状态包括不合法的都初始化为负无穷
f[0][0][0]=0;//从这个状态开始才行,第0天没股票也没有交易,从这个状态转移才合法
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++){
f[i][j][0]=f[i-1][j][0];
if(j>=1)//只有第一天之后才能卖出
f[i][j][0]=max(f[i][j][0],f[i-1][j-1][1]+w[i]);//手上没股票
f[i][j][1]=max(f[i-1][j][0]-w[i],f[i-1][j][1]);//手上有股票
}
int res=0;//枚举手上没有股票每次交易的最大价值
for(int i=0;i<=k;i++)
res=max(res,f[n][i][0]);
cout<<res;
return 0;
}
最长上升子序列优化模板
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N=5e3+10;
int a[N],g[N];
string ans[N];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
g[0]=-1e9;//取最小值
int len=0;
for(int i=1;i<=n;i++){
int l=0,r=len;
while(l<r){
int mid=l+r+1>>1;
if(g[mid]<a[i]) l=mid;
else r=mid-1;
}
len=max(len,l+1);//找到第一个比自己小且最接近自己的下标l
g[l+1]=a[i];
ans[l+1]=ans[l]+to_string(a[i]);//保存最优方案
}
cout<<len<<'\n';
cout<<ans[len];
return 0;
}
Dijkstra带权堆优化模板
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int h[N],ne[N],e[N],w[N],idx,dist[N];
bool visit[N];
typedef pair<int,int> PII;
int n,m,s;//n个点m条边s为起始点
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
void dijkstra(int u){
memset(dist,0x3f,sizeof dist);
dist[u]=0;
priority_queue<PII,vector<PII>,greater<PII>> q;
q.push({0,u});//前面存距离,后面存入口
while(q.size()){
PII a=q.top();
q.pop();
int distance=a.first,enter=a.second;
if(visit[enter]) continue;//如果访问过就跳过循环
visit[enter]=true;//标记访问
for(int i=h[enter];i!=-1;i=ne[i]){
int j=e[i];//记住i存的才是下标
if(dist[j]>distance+w[i]){
dist[j]=distance+w[i];
q.push({dist[j],j});
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,c);
}
dijkstra(s);
for(int i=1;i<=n;i++) cout<<dist[i]<<' ';
return 0;
}
//参考yxc大佬代码
AcWing 1058. 股票买卖 V
给定一个长度为 N 的数组,数组中的第 i 个数字表示一个给定股票在第 i 天的价格。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
输入格式
第一行包含整数 N,表示数组长度。
第二行包含 N 个不超过 10000 的正整数,表示完整的数组。
输出格式
输出一个整数,表示最大利润。
数据范围
1≤N≤1e5
输入样例:
5
1 2 3 0 2
输出样例:
3
来自yxc大佬的分析法
因此我们可以看出状态机入口为手中无货第二天,因为第一天可以买股票,因此要将f[i,0]和f[i,1]初始化为-0x3f3f3f3f(负无穷)然后出口是手中无货第一天和手中无货第二天,二者取一个最大值即可
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+7;
int w[N],f[N][3];//第i天状态为j的最大利润
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
f[0][0]=f[0][1]=-0x3f3f3f3f;
for(int i=1;i<=n;i++){
f[i][0]=max(f[i-1][0],f[i-1][2]-w[i]);//手中有货
f[i][1]=f[i-1][0]+w[i];//手中无货第一天
f[i][2]=max(f[i-1][2],f[i-1][1]);//手中无货第二天
}
cout<<max(f[n][1],f[n][2]);
return 0;
}
P8736 [2020 国 B] 游园安排
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+7;
string ans[N],str[N],f[N];//分别来存最长上升名字的字符串和分割好的名字
int main(){//分割大写名字
string s;
cin>>s;
int cnt=0;
for(auto x:s){
if(isupper(x)){
str[++cnt]=x;
}else{
str[cnt]+=x;
}
}
//把str当作子序列,求最长上升子序列
int len=0;
for(int i=1;i<=cnt;i++){//上升子序列存最优路径模板
int pos=lower_bound(f+1,f+len+1,str[i])-f;
f[pos]=str[i];
len=max(len,pos);
ans[pos]=ans[pos-1]+str[i];
}
cout<<ans[len];//从零开始
return 0;
}