能量項鍊
題目:題目
思路:很久以前寫過的一道題..現在必須要把每一個細節講清楚。
總體思路:區間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;
}