[TJOI2008] 小偷


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轉移方程

\[dp(i,j)= \begin{cases} f(i+1,j)\\ f(i,j-1)\\ f(i,j-sto[i].tim[k])+sto[i].pri[k]; \end{cases} \]

#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,讓我改了下樣例就發現問題了,還是菜呀,因為決策里可以這一秒什么也不干呀,而且,主要原因還是背包體積的變化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM