2006

能量项链

题目:题目

思路:很久以前写过的一道题..现在必须要把每一个细节讲清楚。

总体思路:区间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;
}

猜你喜欢

转载自blog.csdn.net/qq_39824819/article/details/80686379