偷天換日
Luogu P3360
題目背景
神偷對藝術館內的名畫垂涎欲滴準備大撈一把。
題目描述
藝術館由若干個展覽廳和若干條走廊組成。每一條走廊的盡頭不是通向一個展覽廳,就
是分爲兩個走廊。每個展覽廳內都有若干幅畫,每副畫都有一個價值。經過走廊和偷畫都是
要耗費時間的。
警察會在n 秒後到達進口,在不被逮捕的情況下你最多能得到的價值。
輸入輸出格式
輸入格式:
第一行一個整數 n(n≤600)。
第二行若干組整數,對於每組整數(t,x),t 表示進入這個展覽廳或經過走廊要耗費 t
秒的時間,若x>0 表示走廊通向的展覽廳內有x 幅畫,接下來
x對整數(w,c)表示偷一幅價值爲 w 的畫需要 c秒的時間。若
x=0 表示走廊一分爲二。(t,c≤5; x≤30)
輸入是按深度優先給出的。房間和走廊數不超過 300 個。
輸出格式:
僅一個整數,表示能獲得的最大價值。
輸入輸出樣例
輸入樣例#1:
50
5 0 10 1 10 1 5 0 10 2 500 1 1000 2 18 1 1000000 4
輸出樣例#1:
1500
說明
來源:改編
思路
這個題目根據題意我們可以把這個圖實質上轉換爲一棵二叉樹,而且每一個節點的值取決於它所有的兒子節點的值,而且狀態可轉移,沒有後效性,所以我們這個題就是一個樹形動態規劃。
我這個寫法和網上大多數的題解不同,我的算法思路不是很正常,所以希望大家儘量理解。
首先很自然的思路就是我們將輸入數據轉化爲一棵樹。這棵樹上記錄的是每個節點上下的連線或者是每個節點拓展出來的一堆名畫。
需要注意的是,對於路權我們要乘以2,因爲要來回兩次。仍然需要我們注意的是,總時間要減去1,因爲如果大盜和警察在同一時間碰面,還是會被抓。
由於這棵樹的某些節點對應的可能是一堆名畫,然後我們對於每一幅名畫都只有取與不取兩個狀態,所以我們要在這棵樹的末端做一個比較經典01揹包問題。
接下來就是01的狀態轉移方程,這裏的dp[x][t]表示x節點下t時間內能夠得到的最大價值,應該由t減去某一個名畫的時間轉移而來。
dp[x][t]=max(dp[x][t],dp[x][t-paint[x][i][1]]+paint[x][i][0]);
然後就是返回到一個節點後的整合,同樣也是一個動態規劃,狀態轉移方程如下:
for(int t=n;t>=0;t–)
for(int k=t-a[p].w;k>=0;k–)
dp[x][t]=max(dp[son][k]+dp[x][t-a[p].w-k],dp[x][t]);
代碼
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int i,j,m,n,temp;
int hd[601],paint[601][31][2];
int dp[601][601];
struct data
{
int v,w,nxt;
}a[20001];
int r()
{
int ans=0,f=1;
char ch;
while(ch<'0'||ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans*=10;
ans+=ch-'0';
ch=getchar();
}
return ans*f;
}
void add(int x,int y,int z)
{
a[++temp].v=y;
a[temp].nxt=hd[x];
a[temp].w=z;
hd[x]=temp;
}
void build(int x,int fa)
{
int xx=r()*2;
add(fa,x,xx);
int ord=r();
if(ord==0)
{
build(x<<1,x);
build(x<<1|1,x);
}
else
{
paint[x][0][0]=ord;
for(int i=1;i<=paint[x][0][0];i++)
{
paint[x][i][0]=r();
paint[x][i][1]=r();
}
}
}
void dfs(int x)
{
int p,son;
if(paint[x][0][0])
{
for(int i=1;i<=paint[x][0][0];i++)
for(int t=n;t>=paint[x][i][1];t--)
{
dp[x][t]=max(dp[x][t],dp[x][t-paint[x][i][1]]+paint[x][i][0]);
}
}
for(p=hd[x];p;p=a[p].nxt)
{
son=a[p].v;
dfs(son);
for(int t=n;t>=0;t--)
for(int k=t-a[p].w;k>=0;k--)
{
dp[x][t]=max(dp[son][k]+dp[x][t-a[p].w-k],dp[x][t]);
}
}
}
int main()
{
n=r();
n--;
build(1,0);
dfs(0);
cout<<dp[0][n];
return 0;
}
/*
50
5
0
10
1
10
1
5
0
10
2
500
1
1000
2
18
1
1000
4
20
5 0 1 6 0 5 4 0 5 6 3 2 3 2
2 5 5 2 1 2 5 6
20
5 0 1 3 2 0 8 2 4 2 3 1 5 1
20
5 0 3 0 1 2 1 2 5 3 2 1 2 5 3 1 1 2
*/