TJOI2008小偷
題目背景
一位著名的小偷進入了一個充滿寶石的儲藏室,這個儲藏室是由一連串房間構成的,房間的標號從0開始,想進入第i個房間就必須從第i-1個房間進入,如圖:

題目描述
上圖為三個房間的情況,黑色的部分為連通兩個房間的門,從左向右的編號分別為0,1,2…。已知當小偷從第0個門進入儲藏室時,儲藏室的計時系統開始計時,每個門都有自己的關閉時間。每個屋里有不同種類的寶石,對於每種寶石,它的價值和小偷拿走它所耗費的時間也是不同的,為了簡化問題,我們設想小偷在各個屋子之間走動的時間可以忽略不計,而且所有屋子里各種寶石的數量都是無限多的,那么請問小偷在能成功逃出來的情況下,可能獲得寶石的最大價值。
附: 對於每扇門,小偷都必須在嚴格早於此門關閉的時候出來才可以。
輸入格式
每組測試數據的第一行有兩個整數N和M,分別代表儲藏室有N個房間,並且有M種寶石。第二行中會有N個正整數,分別表示第i個門關閉的時間(門的編號從0開始),接下來的M行,每行有三個整數r,v和t,分別代表這種寶石所在的房間編號為r,它的價值為v,小偷拿走它所耗費的時間為t。
輸出格式
輸出小偷在成功逃出儲藏室的情況下獲得寶石的最大價值。
輸入 #1
3 4
9 5 5
0 1 2
1 2 2
2 3 2
2 5 3
輸出 #1
8
樣例說明
我們知道,小偷應該在2時刻拿個3的,再在4時刻拿3的最后逃出到房間一,最后在一房間拿兩個1的寶石,在八秒時逃出。
我們在求解時用到了兩個變量一個是時間,一個是在的屋子。所以我們考慮,以這兩個為變量,造一個函數。
分析算法
我們在每個房間里會有一些寶石,小偷的時間是一定的,寶石可以無限拿,他卻讓我們輸出一個最大值,我們想到了dp。
我們不要被小偷從一號門進去而迷惑,小偷的神行無影,我們理解為小偷出生在最里面的第n間屋子里,在從外面跑,有點類似於紀念品那個題,當然在分析上會難很多。
我們的每一個門,會有自己關門的時間,對吧這就是體積v,每一個寶石就是商品,不要忘了要對關門的時間進行處理
於是乎,套上我的完全背包的模板,就有了1版的代碼
定義
\(dp(i,j)\)表示在第從n到i個房間里,在j的時間內所能拿到的最貴價值
所求
\(dp(1,close[1]-1)\)即為所求我們將第\(1 到 i\)個門中關閉的的最小時間
dp轉移方程
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
using namespace std;
const int Maxdoor=55,Maxthi=110,Maxtime=1100;
struct Node{
int pri[Maxthi],tim[Maxthi],num;
}sto[Maxdoor];
int n,m,x,min1,close[Maxdoor],dp[Maxthi][Maxtime];
int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
while(ch<='9'&&ch>='0'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x;
}
void read_in(){
n=read();m=read();min1=0x3f3f3f3f;
for(int i=1;i<=n;i++){
close[i]=read();
min1=min(min1,close[i]);
close[i]=min(min1,close[i]);
}
for(int i=1;i<=m;i++){
x=read();
x++;
sto[x].num++;
sto[x].pri[sto[x].num]=read();
sto[x].tim[sto[x].num]=read();
}
return;
}
void f(){
for(int i=n;i>=1;i--){
for(int j=1;j<close[i];j++){
dp[i][j]=max(dp[i][j-1],dp[i+1][j-1]);
for(int k=1;k<=sto[i].num;k++){
if(j-sto[i].tim[k]>=0) dp[i][j]=max(dp[i][j],dp[i][j-sto[i].tim[k]]+sto[i].pri[k]);
}
}
}
printf("%d",dp[1][close[1]-1]);
}
int main() {
freopen("1.in","r",stdin);
read_in();
f();
return 0;
}
再經過考慮,我們可以直接降維,因為我們在求\(dp(i,j)\)的時候,只用到了\(dp(i+1)\)和之前的\(dp(i)\),所以我們可以降維
void f(){
for(int i=n;i>=1;i--){
for(int j=1;j<close[i];j++){
dp[j]=max(dp[j],dp[j-1]);
for(int k=1;k<=sto[i].num;k++){
if(j-sto[i].tim[k]>=0) dp[j]=max(dp[j],dp[j-sto[i].tim[k]]+sto[i].pri[k]);
}
}
}
printf("%d",dp[close[1]-1]);
}
其實最開始我們想到代碼的第4行為什么要傳遞最優值給下一個的,但是,連續提交的50pts,讓我改了下樣例就發現問題了,還是菜呀,因為決策里可以這一秒什么也不干呀,而且,主要原因還是背包體積的變化
