zoukankan      html  css  js  c++  java
  • 【BZOJ】【4145】【AMPPZ2014】The Prices

    状压DP/01背包


      Orz Gromah

      容易发现m的范围很小……只有16,那么就可以状压,用一个二进制数来表示买了的物品的集合。

      一种简单直接的想法是:令$f[i][j]$表示前$i$个商店买了状态集合为$j$的商品的最小代价,那么我们转移的时候就需要枚举在第$i$个商店买了哪些商品吗,这样的话带上枚举子集,复杂度就会变成$O(n*3^m)$,并不是我们能够忍受的……

      那么怎么搞呢?我们每次转移的时候,不再枚举子集,而是搞一个类似01背包的东西:(以下来自Gromah)

      我们首先令$f[i][j]=f[i-1][j]+d[i]$,表示到达第$i$个商店。

      然后枚举每个状态$j$,以及每个不在$j$里的物品$k$,令:$$f[i][j+{k}]=min(f[i][j+{k}],f[i][j]+cost[i][k])$$

      这个过程就相当于是进行了一次01背包。

      最后还要令$f[i][j]=min(f[i][j],f[i-1][j])$看看在商店$i$时的购买计划是否划算。

      时间复杂度$O(nm2^m)$,空间复杂度$O(n2^m)$。

     1 /**************************************************************
     2     Problem: 4145
     3     User: Tunix
     4     Language: C++
     5     Result: Accepted
     6     Time:6948 ms
     7     Memory:29440 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 4145
    11 #include<cstdio>
    12 #include<cstring>
    13 #include<cstdlib>
    14 #include<iostream>
    15 #include<algorithm>
    16 #define rep(i,n) for(int i=0;i<n;++i)
    17 #define F(i,j,n) for(int i=j;i<=n;++i)
    18 #define D(i,j,n) for(int i=j;i>=n;--i)
    19 #define pb push_back
    20 using namespace std;
    21 typedef long long LL;
    22 inline int getint(){
    23     int r=1,v=0; char ch=getchar();
    24     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
    25     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
    26     return r*v;
    27 }
    28 const int N=110,M=1<<16,INF=0x3f3f3f3f;
    29 /*******************template********************/
    30  
    31 int n,m,f[N][M],d[N],c[N][20];
    32 int main(){
    33 #ifndef ONLINE_JUDGE
    34     freopen("4145.in","r",stdin);
    35     freopen("4145.out","w",stdout);
    36 #endif
    37     n=getint(); m=getint();
    38     F(i,1,n){
    39         d[i]=getint();
    40         rep(j,m) c[i][j]=getint();
    41     }
    42     rep(j,1<<m) f[0][j]=INF;
    43     f[0][0]=0;
    44     F(i,1,n){
    45         rep(j,1<<m) f[i][j]=f[i-1][j]+d[i];
    46         rep(j,1<<m) rep(k,m){
    47             int s=1<<k;
    48             if ((j&s)==0) f[i][j^s]=min(f[i][j^s],f[i][j]+c[i][k]);
    49         }
    50         rep(j,1<<m) f[i][j]=min(f[i][j],f[i-1][j]);
    51     }
    52     printf("%d
    ",f[n][(1<<m)-1]);
    53     return 0;
    54 }
    55 
    View Code

    4145: [AMPPZ2014]The Prices

    Time Limit: 20 Sec  Memory Limit: 256 MB
    Submit: 70  Solved: 47
    [Submit][Status][Discuss]

    Description

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

    Input

    第一行包含两个正整数n,m(1<=n<=100,1<=m<=16),表示商店数和物品数。
    接下来n行,每行第一个正整数d[i](1<=d[i]<=1000000)表示到第i家商店的路费,接下来m个正整数,
    依次表示c[i][j](1<=c[i][j]<=1000000)。

    Output

    一个正整数,即最小总费用。
     

    Sample Input

    3 4
    5 7 3 7 9
    2 1 20 3 2
    8 1 20 1 1

    Sample Output

    16

    HINT

    在第一家店买2号物品,在第二家店买剩下的物品。


    Source

    [Submit][Status][Discuss]
  • 相关阅读:
    多线程和多进程通信原理
    在树莓派4b上安装 ROS MELODIC 源码安装
    使用Android手机作为树莓派的屏幕
    树莓派桌面设置快捷键
    linux 下使用dd制作启动U盘 安装linux
    manjaro 18.10 install soft
    install slax record
    slax中改变终端字体
    为win10下的linux子系统终端添加powerline
    为ubuntu安装powerline记录
  • 原文地址:https://www.cnblogs.com/Tunix/p/4593542.html
Copyright © 2011-2022 走看看