BZOJ P2071 [POI2004] 过桥【状态压缩】

问题描述

一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制.
所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过.
队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,
我们想知道如何分批过桥能使总时间最少.

输入格式

第一行两个整数: w n 分别表示 桥能承受的最大重量( 100 <= w <= 400 ) 和 n 队员总数( 1 <= n <= 16 ).
接下来 n 行每行两个整数 t w 分别表示每个队员过桥所需时间( 1 <= t <= 50 ) 和 该队员的重量( 10 <= w <= 100 ).

输出格式

输出一个数表示最少的过桥时间.


f [ i ] 表示在 i 的二进制状态下全部过桥所需要的最少时间。

S 1 是状态 S 的一个子集,那么状态 S 一定是通过状态 S 1 与状态 S 2 转移过来的, S 1 S 2 = S

方程表示为:

f [ S ] = m i n ( f [ S 1 ] + M a x T [ S 2 ] ) , S u m W [ S ] <= W

其中 M a x T [ S ] 表示的是 S 状态下的人通过桥的最长时间, S u m W [ S ] 表示的是 S 状态下的人的重量之和。

预处理 M a x T [ ] S u m W [ ] ,然后枚举集合与子集。

#include <bits/stdc++.h>
using namespace std;
const int Inf=1e9;
int N,M,T,C[17],W[17],DP[1<<17],Sum[1<<17],MaxT[1<<17];
int main(){
    int I,J,K,S1,S2;
    scanf("%d%d",&M,&N);
    for(I=1;I<=N;I++){
        scanf("%d%d",&C[I],&W[I]);
    }T=(1<<N)-1;
    for(I=1;I<=T;I++){
        for(J=1;J<=N;J++){
            if((I>>(J-1)&1)==1){
                Sum[I]+=W[J];
                MaxT[I]=max(MaxT[I],C[J]);
            }
        }
    }
    for(I=1;I<=T;I++){
        DP[I]=Inf;
        for(S1=I;S1;S1=(S1-1)&I){
            S2=I^S1;
            if(Sum[S1]<=M){
                DP[I]=min(DP[I],DP[S2]+MaxT[S1]);
            }
        }
    }
    printf("%d",DP[T]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81569440