【題解】垃圾陷阱【記憶化搜索/爆搜+剪枝】

卡門——農夫約翰極其珍視的一條Holsteins奶牛——已經落了到“垃圾井”中。“垃圾井”是農夫們扔垃圾的地方,它的深度爲$ D (2 <=D <= 100)$英尺。

卡門想把垃圾堆起來,等到堆得與井同樣高時,她就能逃出井外了。另外,卡門可以通過吃一些垃圾來維持自己的生命。
每個垃圾都可以用來吃或堆放,並且堆放垃圾不用花費卡門的時間。 假設卡門預先知道了每個垃圾扔下的時間$ T (0 < T <= 1000),以及每個垃圾堆放的高度 H (1 <= H <= 25)和吃進該垃圾能維持生命的時間 F (1 ≤ F ≤ 30)$,要求出卡門最早能逃出井外的時間,假設卡門當前體內有足夠持續10小時的能量,如果卡門10小時內沒有進食,卡門就將餓死。


首先這道題一定是可以搜索的(雖然狀態空間比較大會TLE),先寫一個dfs加深印象

struct E {
	ll t, f, h;
}v[MAX];

bool cmp(E e1, E e2) { return e1.t < e2.t; }

ll D, G, maxAlive, minDepart;

//當前便利到第k個垃圾,能夠活到sur,總高度height
void dfs(ll k, ll sur, ll height) {
	if (height >= D) {
		if (v[k - 1].t < minDepart)minDepart = v[k - 1].t; return;
	}
	if (k >= G) return;//沒法出去
	if (sur < v[k].t)return;//活不到這個垃圾到的時候
	//兩種選擇,吃了還是築高
	dfs(k + 1, sur + v[k].f, height);//吃掉
	//鑄造,此時消耗能量爲與下一個垃圾的時間間隔,增高v[k].h
	dfs(k + 1, sur, height + v[k].h);
}

int main() {
	while (cin >> D >> G) {
		maxAlive = 10, minDepart = inf;
		for (int i = 0; i < G; i++)
			cin >> v[i].t >> v[i].f >> v[i].h, maxAlive += v[i].f;
		sort(v, v + G, cmp);
		dfs(0, 10, 0);
		if (minDepart == inf)cout << maxAlive << endl;
		else cout << minDepart << endl;
	}
}

上述代碼只能得66分,剩下的都TLE了,而對於搜索問題而言,對付狀態空間較大,超時問題的第一個方法無疑是考慮一下記憶化搜索,即如何利用搜索過程中的信息,避免重複狀態的搜索。考慮一下01揹包問題,和這道題有點類似,區別在於01揹包是放還是不放,這裏是吃還是堆起來,所以我們需要一個狀態轉移方程,(你以爲我要用dp),可惜狀態轉移有點難搞,不妨想想如給dfs剪枝,最容易的,對重複狀態進行剪枝,一旦遇到重複狀態直接返回

怎麼做呢,我們只需要創建一個三維數組用來表示出現過的狀態即可

ll exist[105][1000][105];//遍歷到第i個垃圾,還能苟j小時,高度爲k
#define ll int 
#define inf 0x3ffffff
#define MAX 105
#define vec vector<ll>

struct E {
	ll t, f, h;
}v[MAX];

bool cmp(E e1, E e2) { return e1.t < e2.t; }

ll D, G, maxAlive, minDepart;
ll exist[105][1000][105];//遍歷到第i個垃圾,還能苟j小時,高度爲k

//當前便利到第k個垃圾,能夠活到sur,總高度height
void dfs(ll k, ll sur, ll height) {
	if (height >= D) {
		if (v[k - 1].t < minDepart)minDepart = v[k - 1].t; return;
	}
	if (k >= G) return;//沒法出去
	if (sur < v[k].t)return;//活不到這個垃圾到的時候
	if (exist[k][sur][height])return;//重複狀態
	exist[k][sur][height] = 1;
	//兩種選擇,吃了還是築高
	dfs(k + 1, sur + v[k].f, height);//吃掉
	//鑄造,此時消耗能量爲與下一個垃圾的時間間隔,增高v[k].h
	dfs(k + 1, sur, height + v[k].h);
}

int main() {
	while (cin >> D >> G) {
		maxAlive = 10, minDepart = inf;
		memset(exist, 0, sizeof(exist));
		for (int i = 0; i < G; i++)
			cin >> v[i].t >> v[i].f >> v[i].h, maxAlive += v[i].f;
		sort(v, v + G, cmp);
		dfs(0, 10, 0);
		if (minDepart == inf)cout << maxAlive << endl;
		else cout << minDepart << endl;
	}
}

最後:剩一個樣例點沒過,懶得改了,思路就這樣,TLE解決了,主要在於重複狀態有點多。

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