The Prices(类似依赖背包的状压dp)

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]只与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;
}

猜你喜欢

转载自www.cnblogs.com/Lour688/p/13200934.html