BZOJ 4145 浅谈状态压缩动态规划背包问题

来源:互联网 发布:女主是编程天才的小说 编辑:程序博客网 时间:2024/06/06 15:49

这里写图片描述
世界真的很大
状态压缩DP算是我的一大弱点了
其实大概就是在普通的DP过程中,发现转移时会涉及到许多的状态,比如n件不同的物品买了那几件等等,而状态的每一项都是yes or no的关系,就可以用二进制的方法维护起来
其实能这样做得根本原因就是因为计算机本身和二进制有很大关系吧,支持许多位运算的操作,使得01串表示状态成为可能
但是由于int等的限制,这样的状态元素个数往往有限
所以看到元素个数比较小的时候可以考虑状态压缩了,这样
看题先:
description

你要购买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

一个正整数,即最小总费用。

看到m小于等于16就该想到状压了
对于每一个商店有去或不去,每一个商店的每一个物品有买或不买,一共m个物品
挺明显的背包
首先按照背包的套路:f(i,j),表示前i个商店买了j个物品的最小花费,但这道题却不是这么一回事,每个物品在不同的地方有不同的价值,而每个地方都需要处理每个物品买不买,所以说必须细化这个j,让其表示买了哪些物品才行
一共只有16个物品,这里就可以状压了,j的二进制表示一个01串,串的第i个位置为1表示第i个物品已经买了,为0表示没有
考虑转移,每个商店可以去或者不去,去就需要支付路费,再讨论每个物品买不买,而不去的话就直接由上一行转移过来就好
由于不去很好处理,所以优先处理去的情况
首先支付路费,全部加上d[i],然后在对每个状态讨论每个物品在这个地方买还是不买,还是很简单的
然后和不去的情况也就是f(i-1,j)取一下min值就行了
完整代码:

#include<stdio.h>#include<cstring>#include<algorithm>using namespace std;int n,m,ste,f[110][1<<17],c[110][20],d[110];int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        scanf("%d",&d[i]);        for(int j=0;j<m;j++)            scanf("%d",&c[i][j]);    }    ste=1<<m;    memset(f,0x3f3f3f3f,sizeof(f));    f[0][0]=0;    for(int i=1;i<=n;i++)    {        for(int j=0;j<ste;j++)            f[i][j]=f[i-1][j]+d[i];        for(int j=0;j<ste;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<ste;j++)            f[i][j]=min(f[i][j],f[i-1][j]);    }    printf("%d\n",f[n][ste-1]);    return 0;}/*Whoso pulleth out this sword from this stone and anvil is duly born King of all England*/

嗯,就是这样

原创粉丝点击