能量项链
题目:题目
思路:很久以前写过的一道题..现在必须要把每一个细节讲清楚。
总体思路:区间dp,把大区间化为一个个小区间,算把小区间合起来变为大区间
设计状态dp[i][j],表示i-j的项链所产生的最大能量
那么现在思考:假如现在有3颗珠子1、2、3,怎样得到最大价值?(不看环先)
看(1和2、3)或(1、2和3)合并起来哪个大取哪个
[这个样例不是很严谨,只是举个例子↑]
那么每种情况就是在一个地方断开,把一个大区间分成两个小区间
所以动态方程就出来了
for(int len=1;len<=n;len++)//枚举长度
for(int begin=1;begin<=n;begin++)//枚举起点
{
int end=begin+len-1;//得到当前起点的终点
for(r int k=begin;k<=end-1;k++)//枚举断点
dp[begin][end]=max(dp[begin][end],dp[begin][k]+dp[k+1][end]+point[begin].head*point[k].tail*point[end].tail);
}
那么问题产生了,为什么取max的时候是point[begin].head*point[k].tail*
point[end].tail呢?不应该是point[k].head*point[k].tail*point[k+1].head吗?
那么其实是这样的:1-k并不是k个珠子,这时它的含义是1-k个珠子已经合并成了
一个珠子产生的最大价值,同样的,k+1-end表示k+1-end这些珠子合并成了一个
珠子产生的最大价值。
举个例子: 1 2 3 4 假设断点在2 3之间
那么ans=dp[1][2]+dp[3][4]+合并它俩产生的能量
首先算出dp[1][2]就先要合并1、2,那么合并新的珠子H1的头标记其实就是1的头
标记,H1的尾标记就是2的尾标记。同样的,dp[3][4]合并的新珠子H2头标记是3
的头标记,尾标记是4的尾标记。
那么H1、H2合并的能量不就是H1头标记(1的头标记)*H1的尾标记*H2尾标记(4的尾标记)吗?
所以,这个问题解决了
如果按照上述方法写,50分。
为什么?
因为dp代码循环的第二层for(int begin=1;begin<=n;begin++)应改为
for(int begin=1;begin<=2*n;begin++)
为什么?举个例子:1 2 3 4 1 2 3
当算3 4 1 2产生的最大价值时,肯定需要用到dp[1][2],注意!此时的dp[1][2]如
果按照原来的写法是没算过的!因为此程序用到了断环为链,3 4 1 2中的1 2是
1 2 3 4 1 2 3中出现的第二个1 2,而原写法由于从1循环到n只算出了第一个1 2
最后问题解决,注意用到断环为链所以要开2倍数组
代码:
#include <iostream>
#include <cstdio>
#define r register
using namespace std;
//Statement_common
struct Point{int head,tail;}point[301];
int n,ans=-1;
int dp[301][301];
//Statement_fun
int read();
int main()
{
n=read();
for(r int i=1;i<=n;i++)
{
point[i].head=read(),point[i+n].head=point[i].head;
if(i==1) point[n].tail=point[n+n].tail=point[i].head;
else point[i-1].tail=point[i+n-1].tail=point[i].head;
}
for(r int len=1;len<=n;len++)//枚举长度
for(r int begin=1;begin<=2*n;begin++)//枚举起点
{
int end=begin+len-1;//得到当前起点的终点
for(r int k=begin;k<=end-1;k++)//枚举断点
dp[begin][end]=max(dp[begin][end],dp[begin][k]+dp[k+1][end]+point[begin].head*point[k].tail*point[end].tail);
}
for(r int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
cout<<ans;
return 0;
}
int read()
{
int x=0,f=1; char c=getchar();
while(c<'0' || c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*=f;
}
金明的预算方案
题目:题目
思路:没什么好讲的,01背包的变式题。看着代码自然理解。决策由买不买 -> 只买主件好?还是买一个主件+一个附件?还是一个主件+第二个附件?还是一个主件+两个附件
代码
#include<iostream>
#include<cstdio>
#define r register
using namespace std;
//Statement_common
struct Str{int w,p,q,num;}basic[101][101],zhu[101];
int T,n;
int dp[101000],g[101];
//Statement_fun
int read();
int main()
{
T=read(),n=read();
for(int i=1;i<=n;i++)
{
int v,p,q;
v=read(),p=read(),q=read();
p*=v;
if(q==0)
{
zhu[i].num=i;
zhu[i].w=v;
zhu[i].p=p;
}
else
{
g[q]++;
basic[q][g[q]].w=v;
basic[q][g[q]].p=p;
}
}
for(int i=1;i<=n;i++)
if(zhu[i].num!=0)
{
int ww=zhu[i].w;
int pp=zhu[i].p;
int w1=basic[i][1].w,p1=basic[i][1].p;
int w2=basic[i][2].w,p2=basic[i][2].p;
for(int j=T;j>=0;j--)
{
if(j>=ww) dp[j]=max(dp[j],dp[j-ww]+pp);
if(j>=(ww+w1)) dp[j]=max(dp[j],dp[j-ww-w1]+pp+p1);
if(j>=(ww+w2)) dp[j]=max(dp[j],dp[j-ww-w2]+pp+p2);
if(j>=(ww+w1+w2)) dp[j]=max(dp[j],dp[j-ww-w1-w2]+pp+p1+p2);
}
}
cout<<dp[T];
return 0;
}
int read()
{
int x=0,f=1; char c=getchar();
while(c<'0' || c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*=f;
}