The Prices
The Prices 「BZOJ4145」
Decscription
你要购买\(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
Solution
这道题根据\(m\)的范围我们大概知道是状压dp,具体怎么实现的呢,我们还是要提前将路费加上,注意不能加重,之后类似于依赖背包。
具体解释看代码吧
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1<<16,Inf=0x3f3f3f3f;
int n,m,f[105][maxn],a[105][20],d[105];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&d[i]);
for(int j=1;j<=m;++j){
scanf("%d",&a[i][j]);
}
}
int maxs=(1<<m)-1;
for(int i=0;i<=n;++i){
for(int j=0;j<=maxs;++j){
f[i][j]=Inf;
}
}
f[0][0]=0;
for(int i=1;i<=n;++i){
for(int j=0;j<=maxs;++j){//先付路费
f[i][j]=f[i-1][j]+d[i];
}//初始化完之后,不管j是多少,f[i][j]只有1个i的路费,这样避免不同的状态j加重路费
for(int k=1;k<=m;++k){//后买东西
for(int j=0;j<=maxs;++j){
if(j&(1<<(k-1)))continue;
f[i][j|(1<<(k-1))]=min(f[i][j|1<<(k-1)],f[i][j]+a[i][k]);
}
}
for(int j=0;j<=maxs;++j){//决策在哪个商店
f[i][j]=min(f[i][j],f[i-1][j]);//可以用本次循环的f[i][j]传递
}
}
printf("%d\n",f[n][maxs]);
return 0;
}