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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章