[BZOJ2073][POI2004]PRZ
[BZOJ2073][POI2004]PRZ
试题描述
一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过. 队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少.
输入
第一行两个数: w – 桥能承受的最大重量(100 <= w <= 400) 和 n – 队员总数(1 <= n <= 16). 接下来n 行每行两个数分别表示: t – 该队员过桥所需时间(1 <= t <= 50) 和 w – 该队员的重量(10 <= w <= 100).
输出
输出一个数表示最少的过桥时间.
输入示例
100 3 24 60 10 40 18 50
输出示例
42
数据规模及约定
见“输入”
题解
设 f(S) 表示让 S 集合的人过桥所需要的最短总时间。转移的时候枚举子集即可。开始时预处理一下每个集合的重量总和与最大时间,这样状态数乘转移数就变成 3n 的了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 20 #define maxs 65536 #define oo 2147483647 int W, n, tim[maxn], wei[maxn], sumw[maxs], maxt[maxs], f[maxs]; int dp(int S) { if(f[S] < oo) return f[S]; if(sumw[S] <= W) return f[S] = maxt[S]; for(int tS = S - 1 & S; tS; tS = tS - 1 & S) if(dp(tS) < oo && dp(S^tS) < oo) f[S] = min(f[S], dp(tS) + dp(S^tS)); return f[S]; } int main() { W = read(); n = read(); for(int i = 0; i < n; i++) tim[i] = read(), wei[i] = read(); int all = (1 << n) - 1; for(int S = 0; S <= all; S++) for(int i = 0; i < n; i++) if(S >> i & 1) maxt[S] = max(maxt[S], tim[i]), sumw[S] += wei[i]; for(int S = 0; S <= all; S++) f[S] = oo; printf("%d\n", dp(all)); return 0; }