BZOJ4145 [AMPPZ2014]The Prices

题意

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

\(n \leq 100,m \leq 16\)

分析

状压裸题,看题意和数据范围就能想出用\(f(i,s)\)\(i\)个商店买了状态\(s\)的最小花费。

然后是状态转移的一点问题,对每个商店枚举子集会超时,需要快速子集和变换类似的方法来内部转移。

方法就是用每个物品松弛一遍,刷表法。

转移的证明

考虑归纳证明。

首先从前一层转移过来本身就是最优的肯定是合法的。

其次每个状态会向后转移,对后面的状态肯定存在一条最优路径,而这种转移隐式包含了这条最优路径。

所以是正确的。

时间复杂度\(O(n m 2 ^m)\)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
	rg T data=0;
	rg int w=1;
	rg char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-')
			w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		data=data*10+ch-'0';
		ch=getchar();
	}
	return data*w;
}
template<class T>T read(T&x)
{
	return x=read<T>();
}
using namespace std;
typedef long long ll;

co int MAXN=101,MAXM=16;
int n,m;
int d[MAXN],c[MAXN][MAXM];
int f[MAXN][1<<MAXM];

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n);read(m);
	for(int i=1;i<=n;++i)
	{
		read(d[i]);
		for(int j=0;j<m;++j)
			read(c[i][j]);
	}
	memset(f,0x3f,sizeof f);
	f[0][0]=0;
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<1<<m;++j)
			f[i][j]=f[i-1][j]+d[i];
		for(int j=0;j<1<<m;++j)
			for(int k=0;k<m;++k)
				if(~j&(1<<k))
					f[i][j|(1<<k)]=min(f[i][j|(1<<k)],f[i][j]+c[i][k]);
		for(int j=0;j<1<<m;++j)
			f[i][j]=min(f[i][j],f[i-1][j]);
	}
	printf("%d\n",f[n][(1<<m)-1]);
	return 0;
}

posted on 2018-12-04 09:04  autoint  阅读(102)  评论(0编辑  收藏  举报

导航