Description
你要购买m种物品各一件,一共有n家商店,你到第i家商店的路费为d[i],在第i家商店购买第j种物品的费用为c[i][j],
求最小总费用。
Input
第一行包含两个正整数n,m(1<=n<=100,1<=m<=16),表示商店数和物品数。
接下来n行,每行第一个正整数di表示到第i家商店的路费,接下来m个正整数,
依次表示c[i]j。
Output
一个正整数,即最小总费用。
Sample Input
3 4
5 7 3 7 9
2 1 20 3 2
8 1 20 1 1
Sample Output
16
Solution
显然是状压DP,我们可以设f[i][j]表示前i个店铺,买了状态为j的物品的最小价格。
1.若在当前店铺买东西,f[i][j]=min(f[i][j],f[i-1][j]+d[i]),然后再DP选取不同物品的情况
2.若不在当前店铺买东西,f[i][j]=min(f[i][j],f[i-1][j])
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 110
#define M 131072
#define INF 0x7fffffff
#define LL long long
LL f[N][M],ans=INF;
int t[20][M],cnt[M];
int c[N][20],d[N],n,m;
void init()
{
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",&c[i][j]);
}
for (int i=0;i<1<<m;i++)
{
cnt[i]=cnt[i/2]+(i%2==1);
t[cnt[i]][++t[cnt[i]][0]]=i;
}
}
LL min(LL x,LL y)
{
if (x<y) return x;
else return y;
}
int main()
{
init();
for (int j=0;j<=n;j++)
for (int i=0;i<(1<<m);i++)
f[j][i]=INF;
f[0][0]=0;
for (int i=0;i<=n;i++)
{
for (int l=0;l<=m;l++)
for (int k=1;k<=t[l][0];k++)
{
int j=t[l][k];
if (i!=0) f[i][j]=min(f[i][j],f[i-1][j]+d[i]);
if (f[i][j]!=INF)
for (int k=1;k<=m;k++)
if ((j&(1<<(k-1)))==0)
if (i!=0) f[i][j|(1<<(k-1))]=min(f[i][j|(1<<(k-1))],f[i][j]+c[i][k]);
if (i!=0) f[i][j]=min(f[i][j],f[i-1][j]);
}
}
for (int i=1;i<=n;i++)
ans=min(ans,f[i][(1<<m)-1]);
printf("%lld\n",ans);
return 0;
}