分析:设dp[i][j]表示前i个数和为j的方案数,则:
dp[i][j]=dp[i-1][j]+dp[i-1][j-i] (j>=i,dp[n][0]=1)
下面给出两种实现方法(推荐第一种):
#include
#include
#define ll long long
using namespace std;
const int maxn=5005;
ll n,m,dp[maxn];
int main() {
scanf("%lld",&n);
m=n*(n+1)/2;
if(m&1) {
printf("0");
return 0;
}
m/=2;
dp[0]=1;
for(int i=1;i<=n;i++) {
for(int j=m;j>=i;j--) {
dp[j]+=dp[j-i];
}
}
printf("%lld",dp[m]/2);
}
玄学做法:
#include
#include
using namespace std;
const int maxn=5005;
int n,m,dp[40][maxn];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) m+=i;
if(m&1) {
printf("0");
return 0;
}
m/=2;
dp[0][0]=1;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
if(j<i) dp[i][j]=dp[i-1][j];
else dp[i][j]=dp[i-1][j]+dp[i-1][j-i];
}
}
printf("%d",dp[n][m]);
}
分析:
// 设dp[i][j]表示前i天砍前j个树的最大利润
//若砍的树一定,那么一定是增长率大的最后砍
//所以我们选择排序(为了让方案变得有序)
//若砍j,则dp[i][j]=dp[i-1][j-1]+w[j]+(i-1)*k[j]
//若不砍j,则dp[i][j]=dp[i][j-1]
//(j是前i个中k[j]最大的,若不在第j天砍,则算出来一定不是最优,即该方案小于上方案)
//本题想到了不砍j时有可能在前面砍,这样就不能用dp[i][j-1]表示
//而排序则避免了这种决策(未想到)
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=255;
int n,m,dp[maxn][maxn];
struct node{
int w,k;
}a[maxn];
bool cmp(node x,node y) {
return x.k<y.k;
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++) scanf("%d",&a[i].w);
for(int i=1;i<=n;i++) scanf("%d",&a[i].k);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=min(m,n);i++) {
for(int j=i;j<=n;j++) {
dp[i][j]=max(dp[i-1][j-1]+a[j].w+(i-1)*a[j].k,dp[i][j-1]);
}
}
printf("%d\n",dp[min(m,n)][n]);
}
}
分析:和背包问题没有什么关系。
设dp[i][j]表示以第i个数为结尾,取出序列长度为j的方案数
答案取max。
#include
#include
#include
#include
using namespace std;
const int maxn=305;
int n,c,ans,a[maxn],dp[maxn][maxn];
int main() {
memset(dp,0x3f3f3f3f,sizeof(dp));
ans=0x3f3f3f3f;
scanf("%d%d",&n,&c);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=c&&j<=i;j++) {
if(j==1) dp[i][j]=0;
else {
for(int k=1;k<i;k++) {
dp[i][j]=min(dp[i][j],dp[k][j-1]+abs(a[i]-a[k]));
}
}
if(j==c) ans=min(ans,dp[i][j]);
}
}
printf("%d",ans);
}
分析:
这是一道典型的01背包的变形,总容量为x,关键在于如何求出每个物品所占用的空间(价值已确定)
设每个物品花费bi,优惠ci,则sum ci>=sum bi-x
变形得sum(bi-ci)<=x
而bi-ci=bi-(ai-bi)=2*bi-ci, 这就是每个物品占用的空间,其价值为wi,背包容量为x。
注意:2*bi-ci可能为负,我们可以提前把它装进背包里(背包总容量增加)
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=1e6+5,size=505;
ll n,x,dp[maxn],ans,cnt;
struct node{
ll v,w;
}a[size];
int main() {
scanf("%lld%lld",&n,&x);
for(int i=1;i<=n;i++) {
ll ai,bi,vi,wi;
scanf("%lld%lld%lld",&ai,&bi,&wi);
vi=bi*2-ai;
if(vi<=0) {
x-=vi;
ans+=wi;
}
else {
a[++cnt].v=vi;
a[cnt].w=wi;
}
}
for(int i=1;i<=cnt;i++) {
for(int j=x;j>=a[i].v;j--) {
dp[j]=max(dp[j],dp[j-a[i].v]+a[i].w);
}
}
printf("%lld",dp[x]+ans);
}