1. 程式人生 > >Bzoj: 2073 [POI2004]PRZ 題解

Bzoj: 2073 [POI2004]PRZ 題解

就是 put discus aps ring esp 快的 none iostream

2073: [POI2004]PRZ

Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 401 Solved: 296
[Submit][Status][Discuss]

Description

一只隊伍在爬山時碰到了雪崩,他們在逃跑時遇到了一座橋,他們要盡快的過橋. 橋已經很舊了, 所以它不能承受太重的東西. 任何時候隊伍在橋上的人都不能超過一定的限制. 所以這只隊伍過橋時只能分批過,當一組全部過去時,下一組才能接著過. 隊伍裏每個人過橋都需要特定的時間,當一批隊員過橋時時間應該算走得最慢的那一個,每個人也有特定的重量,我們想知道如何分批過橋能使總時間最少.

Input

第一行兩個數: w – 橋能承受的最大重量(100 <= w <= 400) 和 n – 隊員總數(1 <= n <= 16). 接下來n 行每行兩個數分別表示: t – 該隊員過橋所需時間(1 <= t <= 50) 和 w – 該隊員的重量(10 <= w <= 100).

Output

輸出一個數表示最少的過橋時間.

Sample Input

100 3
24 60
10 40
18 50

Sample Output

42
  一道有趣的DP題……   數據範圍這麽小,果斷是狀壓DP啊,但是具體怎麽DP呢?一開始想的是不斷合並兩個狀態數組,然而表示我不會只枚舉所有合法子集的方法,如果暴力枚舉0~i會超時(事實證明真的會超,超15%)。然後想能否像憤怒的小鳥那樣,然而更不靠譜。於是乎我一怒之下靠dfs枚舉所有合法狀態,覺得能過60分就很不錯了,結果萬萬沒想到,A了???490ms??
  然後上網看題解,果然,就是枚舉當前狀態的所有子集,只不過我們不用暴力,用的是位運算:     
1 for(int j=i;j;j=(j-1)&i)
2 {
3     if(sum[j]<=W) f[i]=min(f[i],ti[j]+f[i^j]);
4 }

  j枚舉出來就是i的所有子集。因此,我們還得預處理出來所有狀態的總重量以及時間。然後就相當好搞了。據說復雜度可以證明為3^n。

技術分享
 1 #include <iostream>
 2 #include <cstdlib>
 3 #include <cstdio>
 4 #include <cstring>
 5
#include <algorithm> 6 #include <cmath> 7 #include <queue> 8 #include <map> 9 #include <set> 10 #include <vector> 11 #define N 20 12 using namespace std; 13 int f[1<<17]; 14 int n,t[N],w[N],W; 15 int sum[1<<17],ti[1<<17]; 16 int main() 17 { 18 scanf("%d%d",&W,&n); 19 for(int i=1;i<=n;i++) 20 { 21 scanf("%d%d",&t[i],&w[i]); 22 } 23 memset(f,0x7f,sizeof(f)); 24 f[0]=0; 25 for(int i=1;i<(1<<n);i++) 26 { 27 for(int j=1;j<=n;j++) 28 { 29 if((1<<(j-1))&i) 30 { 31 sum[i]+=w[j]; 32 ti[i]=max(ti[i],t[j]); 33 } 34 } 35 } 36 for(int i=1;i<(1<<n);i++) 37 { 38 for(int j=i;j;j=(j-1)&i) 39 { 40 if(sum[j]<=W) f[i]=min(f[i],ti[j]+f[i^j]); 41 } 42 } 43 printf("%d\n",f[(1<<n)-1]); 44 return 0; 45 }
View Code

Bzoj: 2073 [POI2004]PRZ 題解