一些书上的例题。
矩阵链相乘问题
输入:
5
30 35 15 5 10 20
输出:
11875
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
int n,a[N];
int dp[N][N];//dp[i][j]表示从i-j的最小乘法次数
int main()
{
cin>>n;
for(int i=0;i<=n;i++) cin>>a[i];//0 1是第1各 1 2是第2个 按后一个来看
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<=n;i++) dp[i][i]=0;
//dp
for(int k=2;k<=n;k++)//划分
{
for(int i=1;i<=n;i++)//表示第i个,同时也是两个参数中的第二个参数
{
int j=i+k-1;
for(int t=i;t<j;t++)
{
dp[i][j]=min(dp[i][j],dp[i][t]+a[i-1]*a[t]*a[j]+dp[t+1][j]);
}
}
}
cout<<dp[1][n];
return 0;
}
最大子段和
输入:
6
-2 11 -4 13 -5 -2
输出:
20
O(n2):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10;
int n,a[N];
int ans=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
int temp=0;
for(int j=i;j<=n;j++)
{
temp+=a[j];
ans=max(ans,temp);
}
}
cout<<ans;
return 0;
}
O(n):
用一个例子理解一下O(n):
比如有:-1 -2 -3 4 5 6 7 这一串数字
那么最大子段和肯定是从4-7的和,但我们是一层循环,从数字-1开始的。
我们这样计算:
-1 -2 -3 4 5 6 7
0 0 0 4 5 6 7
由于子段和小于0的按0处理,这样我们就相当于得到了从4-7的子段和。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10;
int n,a[N];
int ans=0;
int dp[N];//dp[i]表示以i为结尾的最大子段和(要有a[i]的)
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int maxn=0;
for(int i=1;i<=n;i++)
{
dp[i]=max(dp[i-1]+a[i],0);
maxn=max(maxn,dp[i]);
}
cout<<maxn;
return 0;
}
01背包dp版
输入:
5 10
2 6
2 3
6 5
5 4
4 6
输出:
15
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+10;
int dp[N][1005];//dp[i][j]表示前i个物品,重量为j的最大价值
int c,n;
int w[N],v[N];
int main()
{
cin>>n>>c;
for(int i=1;i<=n;i++)
{
cin>>w[i]>>v[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(j<w[i]) dp[i][j]=dp[i-1][j];//放不下了
else
{
dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);//拿和不拿
}
}
}
cout<<dp[n][c];
return 0;
}
会场安排问题
输入:
5
1 23
12 28
25 35
27 80
36 50
输出:
3
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n;
int a[N],b[N];
int ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];//开始和结束
sort(a+1,a+1+n);
sort(b+1,b+1+n);
int j=1;
for(int i=1;i<=n;i++)
{
if(a[i]>=b[j])//开始的在已有场次的后面可以沿用
{
j++;//结束时间就可以往后推
}
else
{
ans++;
}
}
cout<<ans;
return 0;
}
ps:以下是我们学校OJ上的期末复习题。
二分搜索
一道模板二分题。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110000+10;
int a[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int t;
while(cin>>t)
{
if(!t) break;
int l=1,r=n;
int mid=(l+r)/2;
int flag=0;
while(l<r)
{
mid=(l+r)/2;
if(a[mid]==t)
{
flag=1;break;
}
else if(a[mid]<t)
{
l=mid+1;
}
else r=mid-1;
}
if(flag) cout<<"yes";
else cout<<"no";
cout<<endl;
}
return 0;
}
购书计划(贪心or直接模拟)
其他测试数据:
输入样例:
500 300 30 5
30 40 40 30 60
输出样例:
5
输入样例:
500 450 30 5
60 70 80 80 90
输出样例:
0
输入样例:
500 300 70 5
30 50 60 40 35
输出样例:
0
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+10;
//他打算把购买机票后剩下的钱都用来买书。
//小明也不打算买过于便宜的书。
int r,p,b,n;
int a[N];
int main()
{
cin>>r>>p>>b>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+1+n);
int ans=0;int flag=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=b)
{
flag=i;break;
}
}
r-=p;
if(flag==0)
{
ans=0;
cout<<ans;
return 0;
}
for(int i=flag;i<=n;i++)
{
if(r>=a[i])
{
r-=a[i];
ans++;
}
else break;
}
cout<<ans;
return 0;
}
带书计划(01背包+字典序)
Hint
《introduction_algorithm》《algorithm_training》和《data_struct》《beauty_of_math》两种组合都可以获得最大值,取字典序最小的按字典序输出。
注意:
- 相同的重量要找字典序小的情况——对比字典序的时候可能出现拿的书的不同个数
- 注意-1的特判
感觉写的有点复杂,不知道有没有更好的解法。
2022.1.6补充,有更好的解法:看这里
string可以直接字典序比较,用结构体排序+回溯法。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct node
{
string name;
int zdx;
int a;//weight
}a[105];
int n,m;
string name[105];
int dp[105];
vector<int> v[N];//标记在当前价格下这本书是否带了:用字典序的编号
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].name>>a[i].a;
name[i]=a[i].name;
}
sort(name+1,name+1+n);
//把字典序赋值
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i].name==name[j])
{
a[i].zdx=j;
break;
}
}
}
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
//要再拿
if(dp[j]+a[i].a<=m)
{
dp[i]=max(dp[i],dp[j]+a[i].a);
if(v[dp[i]].empty())//之前没有
{
v[dp[i]]=v[dp[j]];
v[dp[i]].push_back(a[i].zdx);
}
else
{
vector<int> temp;
temp=v[dp[j]];
temp.push_back(a[i].zdx);
sort(v[dp[i]].begin(),v[dp[i]].end());
sort(temp.begin(),temp.end());
int flagg=0;//1表示现在的好 -1表示之前的好
//注意 长度可能不一样
for(int k=0;k<min(temp.size(),v[dp[i]].size());k++)
{
if(temp[k]<v[dp[i]][k])
{
flagg=1;break;
}
else if(temp[k]>v[dp[i]][k])
{
flagg=-1;break;
}
}
if(flagg==1)
{
v[dp[i]]=temp;
}
}
}
}
//cout<<dp[i]<<endl;
}
int maxn=-1;
for(int i=1;i<=n;i++)
{
maxn=max(maxn,dp[i]);
}
sort(v[maxn].begin(),v[maxn].end());
for(auto x:v[maxn])
{
cout<<name[x]<<endl;
}
if(v[maxn].empty()) cout<<-1;
return 0;
}