bzoj 4145 [AMPPZ2014]The Prices

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值