您的位置:首页 > 其它

动态规划的背包问题

2018-01-28 11:57 218 查看
背包问题有许多

01背包:一种只能取一次

多重背包:一种可以取有限的次数

完全背包:一种可以取无数次



最近做dp做的快吐了···

先来个简单的01背包

luogu1060 自己找题吧···

直接上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,v[30],w[30],dp[26][30000],ans;

int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>v[i]>>w[i];
}
for(int i=1;i<=m;i++)
for(int j=i;j<=n;j++)
{
if(j-v[i]>=0)
dp[i][j]=max(dp[i-1][j-v[i]]+w[i]*v[i],dp[i-1][j]);//dp方程
else dp[i][j]=dp[i-1][j];
}
for(int i=1;i<=m;i++)
{
for(int j=i;j<=n;j++)
ans=max(ans,dp[i][j]);
}
cout<<ans<<endl;
return 0;
}


luogu1164

求方案数的

也是01背包

#include<iostream>
#include<cstdio>
#define maxn 10005
using namespace std;
int n,m,a[maxn],dp[maxn],mx;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
dp[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=a[i];j--)//注意这里要倒着推,才能保证不会重复算
{
dp[j]=dp[j-a[i]]+dp[j];
}
cout<<dp[m]<<endl;
return 0;
}


然后再来个完全背包的吧

luogu1616

#include<iostream>
#include<cstdio>
using namespace std;
int t,m,v[10005],tim[10005],dp[100005];
int main()
{
cin>>t>>m;
for(int i=1;i<=m;i++)
{
cin>>tim[i]>>v[i];
}
for(int i=1;i<=t;i++)//以时间为第一重循环
{
int maxn=0;
for(int j=1;j<=m;j++)
{
if(i-tim[j]>=0)
maxn=max(maxn,dp[i-tim[j]]+v[j]);
else maxn=max(maxn,dp[i]);//dp方程
}
dp[i]=maxn;//算出每个时间能达到的最大值
}
cout<<dp[t]<<endl;
return 0;
}


然后多重背包

luogu1077 数据范围不大暴力枚举就行

多重背包好像可以二进制拆分优化

但这道求方案数的好像不太可以

粘代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 105
#define md 1000007
using namespace std;
int n,m,a[maxn],dp[maxn][maxn];

int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=0;i<=m;i++) dp[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=j;k>=j-a[i];k--)
{
if(k>=0)
{
dp[i][j]+=dp[i-1][k],dp[i][j]%=md;
}
else break;
}
printf("%d\n",dp
[m]);
return 0;
}


咳咳

这么多水题

来个难的

luogu1880石子合并

令人窒息的操作

其实就是线性dp

遇到环的问题先复制转化成线

然后倒推起点

一段一段搜

取各个段中间的值都遍历一遍

找最大值和最小值

其他的线性dp和这个思路差不多都

#include<iostream>
#include<cstdio>
#define INF 0x7fffff
using namespace std;
int n,a[205],b[205],mx,mn,dps[2005][2005],dpl[2005][2005];

int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],a[i+n]=a[i];//复制
for(int i=1;i<=2*n;i++) b[i]=b[i-1]+a[i];//前缀和
for(int i=2*n-1;i>=1;i--)//倒推起点
for(int j=i+1;j<=i+n-1;j++)//长度为n的一段
{
dps[i][j]=INF;
for(int k=i;k<j;k++)//遍历中间值
{
dps[i][j]=min(dps[i][j],dps[i][k]+dps[k+1][j]+b[j]-b[i-1]);//dp方程
dpl[i][j]=max(dpl[i][j],dpl[i][k]+dpl[k+1][j]+b[j]-b[i-1]);
}
}
mn=INF;
for(int i=1;i<=n;i++)
{
mx=max(mx,dpl[i][i+n-1]);
mn=min(mn,dps[i][i+n-1]);
}
cout<<mn<<endl<<mx<<endl;
return 0;
}


以及luogu1057

也是一个环的问题

不过这个问题有些不同

思路就是这个人的等于左边传过来的加上右边传过来的

注意第一个和最后一个要单独拿出来

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,dp[35][35];

int main()
{
cin>>n>>m;
dp[1][0]=1;
for(int i=1;i<=m;i++)
{
dp[1][i]=dp[2][i-1]+dp
[i-1];
dp
[i]=dp[n-1][i-1]+dp[1][i-1];
for(int j=2;j<n;j++)
dp[j][i]=dp[j-1][i-1]+dp[j+1][i-1];
}
cout<<dp[1][m]<<endl;
return 0;
}


luogu1063

依然是环的问题

和石子合并一个思路

也是一段段的推

在这提供两种写法

第一种:

以i为一段的长度,j为起点,i+j-1为终点,k为中间值

第二种:

以i为起点,j为终点,每段都是n,k为中间值

两种思想都一样

但是还是认为第二种思路更清晰

第一种比较迷容易写错

#include<iostream>
#include<cstdio>
#define maxn 300
using namespace std;
int n,a[maxn],dp[maxn][maxn],mx;

int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i+n]=a[i];
}
for(int i=2;i<=n+1;i++)
for(int j=1;j<=2*n && i+j-1<=2*n ;j++)
for(int k=j+1;k<i+j-1;k++)
{
int r=i+j-1;
dp[j][r]=max(dp[j][r],dp[j][k]+dp[k][r]+a[j]*a[k]*a[r]);
mx=max(mx,dp[j][r]);
}//第一种
for(int i=2*n-1;i;i--)
for(int j=i+1;j<i+n && j<=2*n;j++)
for(int k=j-1;k>=i;k--)//这里正反无所谓
{
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]);
mx=max(dp[i][j]);
}//第二种
cout<<mx<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  背包问题