POJ 1170 Shopping Offers (状压DP)

首先确定dp的状态是购买当前这些物品所需要的最小价值,显然对于每个打折方案,都可以到达一个其他状态,也当前物品就是加上那个方案新买的物品,并获得一个新价值。用这个新价值去更新新状态,就是一个典型的完全背包。

剩下的就是找到一个方法来表示每个状态。因为每种物品最多5个,所以想到用6进制来状压(注意可能的数量是0~5,6种情况,不是5种)。最多5种物品,因此状态数最多6^5。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#include <algorithm>
#define INF 100000000
int dp[47000];
int num[7];
int prc[7];
int Hs[1005];
int six[10];
void change1(int a[],int b){
	for(int i=0;i<6;i++){
		a[i]=b%6;
		b/=6;
	}
}


struct offer{
	int c[7];
	int v;
}O[105];

int OK(offer a,int b){
	int t[7];
	for(int i=1;i<=6;i++){
		if(b%6+a.c[i]>num[i]) return -1;
		t[i]=b%6+a.c[i];
		b/=6;
	}
	int res=0,cur=1;
	for(int i=1;i<=6;i++){
		res+=t[i]*cur;
		cur*=6;
	}
	return res;
}
int N,S,M;
int main(){
	six[1]=6;
	for(int i=2;i<=6;i++) six[i]=six[i-1]*6;
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		int c,k,p;
		scanf("%d%d%d",&c,&k,&p);
		Hs[c]=i;num[i]=k;prc[i]=p;
	}
	for(int i=0;i<six[N];i++){
		dp[i]=INF;
		int cur[6];
		change1(cur,i);
		bool flag=0;
		for(int j=0;j<6;j++){
			if(cur[j]>num[j+1]){
				flag=1;
				break;
			}
		}
		if(flag) continue;
		dp[i]=prc[1]*cur[0];
		for(int j=1;j<6;j++){
			dp[i]+=prc[j+1]*cur[j];
		}
	}

	scanf("%d",&S);
	for(int i=0;i<S;i++){
		int n;
		scanf("%d",&n);
		memset(O[i].c,0,sizeof(O[i].c));
		for(int j=0;j<n;j++){
			int x,y;
			scanf("%d%d",&x,&y);
			O[i].c[Hs[x]]=y;
		}
		scanf("%d",&O[i].v);
	}

	for(int i=0;i<six[N];i++){
		if(dp[i]==INF) continue;
		for(int j=0;j<S;j++){
			int tmp=OK(O[j],i);
			if(tmp!=-1){
				dp[tmp]=min(dp[tmp],dp[i]+O[j].v);
			}
		}
	}
	int res=0,cur=1;
	for(int i=1;i<=N;i++){
		res+=cur*num[i];
		cur*=6;
	}
	printf("%d\n",dp[res]);
	return 0;
}



你可能感兴趣的:(状压dp)