【BZOJ4145】The Prices

【题意】

你要购买 m m m种物品各一件,一共有 n n n家商店,你到第i家商店的路费为 d [ i ] d[i] d[i],在第 i i i家商店购买第 j j j种物品的费用为 c [ i ] [ j ] c[i][j] c[i][j],求最小总费用。

【题解】

很容易想到状态转移方程。设 f [ i ] [ S ] f[i][S] f[i][S]为去了前 i i i家商店,已购买物品的集合为S,则:
f [ i ] [ S ] = m i n { f [ i − 1 ] [ S ] , f [ i − 1 ] [ S ′ ] ∣ S ′  S } f[i][S]=min\{f[i-1][S], f[i-1][S']|S' \varsubsetneqq S \} f[i][S]=min{f[i1][S],f[i1][S]SS}
枚举子集的复杂度为 O ( 3 m ) O(3^m) O(3m),故总的复杂度为 O ( n 3 m ) O(n3^m) O(n3m)。目测会T。
如果我们把每一个商店看成一个物品,它卖的东西看成这个物品被使用的状态。若把这个状态看成一个二进制位,则每一位若为 1 1 1则有一个权值(相应商品的价格),那么抛开路费,在一家商店里的转移就类似于完全背包。
实现时,我们可以将上一行的状态搬下来并加上 d [ i ] d[i] d[i],然后进行同层的完全背包转移,再与不购买物品的选择作比较取最小值。
此题中,单层完全背包的时间复杂度为 O ( n m ) O(nm) O(nm),故总时间复杂度为 O ( n m 2 m ) O(nm2^m) O(nm2m)

【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mm = 17, mn = 105;
int f[mn][1 << mm], d[mn];
int c[mn][mm];
int main()
{
    int n, m, i, j, k;
    scanf("%d%d", &n, &m);
    for(i = 1; i <= n; i++)
    {
        scanf("%d", &d[i]);
        for(j = 0; j < m; j++)
            scanf("%d", &c[i][j]);
    }
    memset(f, 0x3f, sizeof f);
    int lim = 1 << m;
    f[0][0] = 0;
    for(i = 1; i <= n; i++)
    {
        for(j = 0; j < lim; j++)
            f[i][j] = f[i - 1][j] + d[i];
        for(k = 0; k < m; k++)//枚举物品
            for(j = 1; j < lim; j++)
                if(j & (1 << k))
                f[i][j] = min(f[i][j], f[i][j ^ (1 << k)] + c[i][k]);
        for(j = 0; j < lim; j++)
            f[i][j] = min(f[i][j], f[i - 1][j]);
    }
    printf("%d\n", f[n][lim - 1]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值