1)01背包问题(https://www.acwing.com/problem/content/2/)
//二维代码:
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
//不用赋初值,从0个物品中选体积不超过j或者从i个物品中选体积不超过0,最大价值就是0
ll f[nl][nl];//从前i个里面选,体积不超过j的最大价值
//状态计算f[i][j]=f[i-1][j]+f[i-1][j-v[i]]+w[i]
//从最后一个物品选不选划分成两块
//f[i-1][j](不选第i个物品,体积不超过j,这种情况在上一层即i-1层就选好了)这里着重搞清楚层次之间的关系。
//f[i-1][j-v[i]]+w[i](选第i个物品,即从i-1个物品中选,体积不超过j-v[i],最后再加上w[i])
ll v[nl];//第i个物品的体积
ll w[nl];//第i个物品的价值
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
for(i=1;i<=n;i++){
cin>>v[i];
cin>>w[i];
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=v[i]){
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
}
}
cout<<f[n][m];//从n个物品中选体积不超过m的最大价值。
}
//一维:不优化时间,优化空间
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
ll f[nl];
ll v[nl];
ll w[nl];
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
for(i=1;i<=n;i++){
cin>>v[i];
cin>>w[i];
}
for(i=1;i<=n;i++){
for(j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);//搞清楚层序关系,
//每次运算都用到的是上一层数据。
}
}
cout<<f[m];
}
2)完全背包问题(https://www.acwing.com/problem/content/description/3/)每个物品可以选无限个。
//纯暴力,会超时,复杂度1e9
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
//不用赋初值,从0个物品中选体积不超过j或者从i个物品中选体积不超过0,最大价值就是0
ll f[nl][nl];//从前i个里面选,体积不超过j的最大价值
//状态计算f[i][j]=f[i-1][j]+f[i-1][j-k*v[i]]+k*w[i]
//从第i个物品选几个划分成k+1块
//f[i-1][j](不选第i个物品,体积不超过j,这种情况在上一层即i-1层就选好了)这里着重搞清楚层次之间的关系。
//f[i-1][j-k*v[i]]+k*w[i](选K个第i个物品,即从i-1个物品中选,体积不超过j-k*v[i],最后再加上k*w[i])
ll v[nl];//第i个物品的体积
ll w[nl];//第i个物品的价值
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
for(i=1;i<=n;i++){
cin>>v[i];
cin>>w[i];
}
for(i=1;i<=n;i++){
for(j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(ll k=1;k*v[i]<=m;k++){
if(j>=k*v[i]){
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<f[n][m];//从n个物品中选体积不超过m的最大价值。
}
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
//不用赋初值,从0个物品中选体积不超过j或者从i个物品中选体积不超过0,最大价值就是0
ll f[nl][nl];//从前i个里面选,体积不超过j的最大价值
//状态计算f[i][j]=f[i-1][j]+f[i][j-v[i]]+w[i]
//与之前暴力的做法不同,此时进行了状态计算的简化
// f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i],f[i-1][j-2*v[i]]+2*w[i]....)
//f[i][j-v[i]]=max( ,f[i-1][j-v[i]], f[i-1][j-2*v[i]]+w[i]....)
//对比可以发现f[i][j]与f[i][j-v[i]]差了一个w[i],(当然还差了f[i-1][j],之后取max补上就行)
ll v[nl];//第i个物品的体积
ll w[nl];//第i个物品的价值
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
for(i=1;i<=n;i++){
cin>>v[i];
cin>>w[i];
}
for(i=1;i<=n;i++){
for(j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=v[i]){
f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
}
}
}
cout<<f[n][m];//从n个物品中选体积不超过m的最大价值。
}
3)多重背包问题 (物品数量有了限制,其实再加一个判断条件就行)(https://www.acwing.com/problem/content/4/)
//纯暴力,因为数据小不超时
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
//不用赋初值,从0个物品中选体积不超过j或者从i个物品中选体积不超过0,最大价值就是0
ll f[nl][nl];//从前i个里面选,体积不超过j的最大价值
//状态计算f[i][j]=f[i-1][j]+f[i-1][j-k*v[i]]+k*w[i]
//从第i个物品选几个划分成k+1块
//f[i-1][j](不选第i个物品,体积不超过j,这种情况在上一层即i-1层就选好了)这里着重搞清楚层次之间的关系。
//f[i-1][j-k*v[i]]+k*w[i](选K个第i个物品,即从i-1个物品中选,体积不超过j-k*v[i],最后再加上k*w[i])
ll v[nl];//第i个物品的体积
ll w[nl];//第i个物品的价值
ll s[nl];//第i个物品的数量
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
for(i=1;i<=n;i++){
cin>>v[i];
cin>>w[i];
cin>>s[i];
}
for(i=1;i<=n;i++){
for(j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(ll k=1;k*v[i]<=m&&k<=s[i];k++){
if(j>=k*v[i]){
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
}
}
}
}
cout<<f[n][m];//从n个物品中选体积不超过m的最大价值。
}
4)多重背包问题 (对时间复杂度有了要求)
(https://www.acwing.com/problem/content/description/5/)
//f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]...f[i-1][j-s*v[i]]+s*w[i])
//f[i][j-v[i]]= max( f[i-1][j-v[i]] ...f[i-1][j-k*v[i]]+s*w[i],f[i-1][j-(s+1)*v[i]])
//因为多了一个(s+1),所以不能像完全背包问题那样优化
//将多重背包问题转移成01背包问题
//运用了1 2 4 2^n可以表示2^(n+1)-1内所有数的性质
//将这些物品分成多份,每份是2^k,这样就可以分开存储体积和价值,就转换成01背包问题
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e5+5;
using namespace std;
ll v[nl];
ll w[nl];
ll f[nl];//运用二维会超内存
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
ll cnt=1;
for(i=1;i<=n;i++){
ll a,b,s;
cin>>a>>b>>s;
ll k=1;
while(k<=s){
v[cnt]=k*a;
w[cnt]=k*b;
s-=k;
k*=2;
cnt++;
}
if(s>0){//如果s最后大于0,说明那就把s再加上就可以表示原来S内所有数了
//性质证明:针对10 可以分为1 2 4 3
//1~4可以表示1~7,加上3就可以表示4到10,这样1~7 4~10都可以表示,则1~10就可以表示了
v[cnt]=s*a;
w[cnt]=s*b;
cnt++;
}
}
for(i=1;i<=cnt-1;i++){
for(j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m];
}
5)分组背包问题(https://www.acwing.com/problem/content/9/)
//从第i个组选哪一个划分成s[i]块(与多重背包区别)
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e2+5;
using namespace std;
ll v[nl][nl],w[nl][nl],s[nl];
ll f[nl][nl];
ll k;
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
ll k=1;
for(i=1;i<=n;i++){
cin>>s[i];
for(j=0;j<s[i];j++){
cin>>v[i][j];
cin>>w[i][j];
}
}
for(i=1;i<=n;i++){
for(j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(k=0;k<s[i];k++){
if(v[i][k]<=j){
f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}
}
}
}
cout<<f[n][m];
}
6) 数字三角形(https://www.acwing.com/problem/content/description/900/) 赋初值很重要
//状态计算:f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j]
//状态划分:到第i,j个点的路径最大值取决于i-1,j-1个点与i-1,j个点
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e4+5;
const ll inf=1e9;
using namespace std;
ll a[nl][nl];
ll f[nl][nl];
ll res=0;
int main(){
ll n;
cin>>n;
ll i,j;
for(i=1;i<=n;i++){//从1开始,避免i-1<0的情况出现
for(j=1;j<=i;j++){//同理
cin>>a[i][j];
}
}
for(i=1;i<=n;i++){//有些情况f[i-1][j]不存在,所以直接赋值负无穷小
for(j=0;j<=i+1;j++){
f[i][j]=-inf;
}
}
f[1][1]=a[1][1];//赋初值
for(i=2;i<=n;i++){
for(j=1;j<=i;j++){
f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);//状态划分:到第i行第j列的路径最大值,可以划分成到第i-1行第j-1列和到第i-1行第j列的最大值+a[i][j];
}
}
res=-inf;//别忘记res赋初值,因为题目中路径值可能为负数
for(i=1;i<=n;i++){
res=max(res,f[n][i]);//最后一层遍历
}
cout<<res;
}
7)最长上升子序列(https://www.acwing.com/problem/content/897/)
//状态计算:f[i]=max(f[i],f[j])1<=j<=i
//状态划分:到第i个数的最长序列等于*它之前的*所有比它小的数*的最长序列+1
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e4+5;
const ll inf=1e9;
using namespace std;
ll a[nl];
ll f[nl];
ll res=0;
int main(){
ll n;
cin>>n;
ll i,j;
for(i=1;i<=n;i++){
cin>>a[i];
}
for(i=1;i<=n;i++){
f[i]=1;
for(j=1;j<=i;j++){
if(a[j]<a[i]){
f[i]=max(f[i],f[j]+1);
}
}
}
ll res=1;
for(i=1;i<=n;i++){
res=max(res,f[i]);
}
cout<<res;
}
8)最长公共子序列(https://www.acwing.com/problem/content/899/)
//状态计算:f[i][j]=f[i-1][j-1],f[i-1][j-1]+1,f[i-1][j],f[i][j-1]
//状态划分:a中到第i个字母和b中到第j个字母的最长公共系序列的长度
//不选第a[i]和b[j],选a[i]不选b[j],不选a[i]选b[j],a[i]和b[j]都选
//第二种和第三种的状态不好表示,但可以用f[i][j-1]。。代替,这里运用了集合的包含关系。
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e4+5;
const ll inf=1e9;
using namespace std;
ll f[nl][nl];
char a[nl],b[nl];
int main(){
ll n,m;
cin>>n>>m;
ll i,j;
scanf("%s%s",a+1,b+1);
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
f[i][j]=max(f[i][j-1],f[i-1][j]);
if(a[i]==b[j]){
f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
}
}
cout<<f[n][m];
}
9)最短编辑距离 (https://www.acwing.com/problem/content/description/904/)
//状态计算:f[i][j]=min(f[i][j-1]+1(增操作),f[i-1][j]+1(删操作))
//f[i][j]=min(f[i][j],f[i-1][j-1]+1/+0(改操作))
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e4+5;
const ll inf=1e9;
using namespace std;
ll f[nl][nl];
char a[nl],b[nl];
int main(){
ll n,m;
cin>>n;
scanf("%s",a+1);
ll i,j;
cin>>m;
scanf("%s",b+1);
for(i=1;i<=n;i++){
f[i][0]=i;//让a的前i个字符与b的前0个字符匹配需要i次删操作
}
for(i=1;i<=m;i++){
f[0][i]=i;//让a的前0个字符与b的前i个字符匹配需要i次增操作
}
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
if(a[i]==a[j]){
f[i][j]=min(f[i-1][j-1],f[i][j]);
}else{
f[i][j]=min(f[i-1][j-1]+1,f[i][j]);
}
}
}
cout<<f[n][m];
}
10编辑距离(https://www.acwing.com/problem/content/901/)
这是个冤种题
memset()会占用复杂度。。。。
求字符串长度要用.size()别用sizeof()
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
using namespace std;
ll n,m;
string a[nl],b[nl];
ll f[nl][nl];
ll dp(string al,string bl,ll nn,ll mm){
for(ll i=1;i<=nn;i++){
f[i][0]=i;//赋初值,a中前i个和b中前0个匹配,需要进行i次删操作。
}
for(ll i=1;i<=mm;i++){
f[0][i]=i;
}
for(ll i=1;i<=nn;i++){
for(ll j=1;j<=mm;j++){
f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);//括号前面代表删操作(a中前i-1和b中前j个匹配),后面代表增操作
if(al[i]==bl[j]){
f[i][j]=min(f[i][j],f[i-1][j-1]);//代表改操作
}else{
f[i][j]=min(f[i][j],f[i-1][j-1]+1);
}
}
}
return f[nn][mm];
}
int main(){
cin>>n;
cin>>m;
for(ll i=0;i<n;i++){
string s=" ";
cin>>a[i];
a[i]=s+a[i];
}
for(ll i=0;i<m;i++){
string s=" ";
ll k;
cin>>b[i]>>k;
//memset(f,0,sizeof(f));
b[i]=s+b[i];
ll num=0;
for(ll j=0;j<n;j++){
//memset(f,0,sizeof(f));//占用时间复杂度
ll z=dp(a[j],b[i],a[j].size(),b[i].size());
//cout<<z<<" ";
if(z<=k){
num++;
}
}
cout<<num<<endl;
}
}
11)石子合并(https://www.acwing.com/problem/content/284/)
//状态计算:f[l][r]=min(f[l][k]+f[k+1][r]+a[r]-a[l-1],f[l][r]);l<=k<=r-1
//划分区间,看区间最后是从哪两个区间合并而来
#include<bits/stdc++.h>
#define ll long long
const ll nl=1e3+5;
const ll inf=1e9;
using namespace std;
ll a[nl];
ll f[nl][nl];
int main(){
ll n,m;
cin>>n;
for(ll i=1;i<=n;i++){
cin>>a[i];
a[i]+=a[i-1];
}
//i表示区间长度,区间长度为1时不用赋初值,因为代价为0
for(ll i=2;i<=n;i++){
for(ll j=1;j+i-1<=n;j++){
ll l=j,r=j+i-1;
f[l][r]=inf;//赋初值,不然都是0。。。
for(ll k=l;k<=r-1;k++){
f[l][r]=min(f[l][k]+f[k+1][r]+a[r]-a[l-1],f[l][r]);
}
}
}
cout<<f[1][n];
}