P2473 [SCOI2008]獎勵關 - 期望(神奇的倒推) - 狀態壓縮

大力模擬各種情況
k * n 的模擬出每次可能拋什麼出來
設f[k][s] 爲前k個物品狀態爲s時得分最大值
但是有問題啊,比如 113 和 133,狀態都是f[3][101],難道我還要多開一維存下不同情況?,不行吧?

還是說取最大的?,這相當於後面選擇的物品,對前面的狀態強行選擇了一下,但是並不是說113比133大,我就捨棄133,我捨棄133就相當於,走到這一步,後面直接都不走了,如果說我們要取最優解,顯然可以取最大的,捨棄掉不優的,但是對於一種需要遞推,或者是求期望的這種要求把所有可能都走一遍取平均值的,不能捨棄吧?捨棄這個狀態,就好像求平均數的時候,你捨棄了幾個比較小的數,那麼無論如何這個平均數都是錯誤的吧?

這種轉移感覺就很麻煩的,試着倒推,不是說拿東西麼,我們把過程倒過來,扔東西,從第n個階段每個階段扔一個東西並獲得收益,也就是說f[k][s]表示前k個狀態爲s時,k+1 ~ n輪獲得的期望收益,而答案是f[1][0]

這代表了什麼呢,爲什麼倒推的時候,是由前面的狀態決定能不能“扔”?
實際上,假設有一種方案是01011吧,這個假設說明最後物品集合就是01011,只不過我們在預知了這個集合的情況下,把過程逆過來,那麼我這個物品能不能扔,意味着這個物品(在正向過程中)能不能選,所以狀態的設定是從k+1輪到最後的收益,但其物品集合卻是第1 ~ k 輪選擇的物品集合。從i + 1 到 i,只要f[i+1][j]中的j滿足i的需求,就可以轉移

有很多無用的狀態也會發生轉移,不過在取max的基礎上這些轉移無法到達最後,貌似狀壓有一部分題的轉移都是這樣的,一些狀態本來不應該存在,但是在取max或者min的時候這些狀態一定比那些應該存在的狀態更不優,所以如果能證明出這點,不用考慮那麼多直接轉移就好

期望的和等於和的期望是劃分階段的關鍵條件,因爲不同階段的期望相加之後就是整個過程的期望,所以可以遞推

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
struct object{
	double val;
	int si;
}ob[MAXN];
double ans,f[101][1<<15];
int n,k;

int main() {
	scanf("%d%d", &k, &n);
	for(int i=1; i<=n; i++) {
		cin >> ob[i].val;
		int si = 0;
		while(1) {
			int pos = 0;
			scanf("%d", &pos);
			if(!pos) break;
			si |= 1 << (pos-1);
		}
		ob[i].si = si; 	
	}
	for(int i=k; i; i--) {
		for(int j=0; j<1<<n; j++) {
			for(int x=1; x<=n; x++) {
				 int si = ob[x].si;
				 if((j&si) == si) {
				 	f[i][j] += max(f[i+1][j], f[i+1][j|(1<<(x-1))] + ob[x].val);
				 } else {
				 	f[i][j] += f[i+1][j];
				 }
			}
			f[i][j] /= n;
		}
	}
	printf("%.6lf", f[1][0]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章