题目链接:https://vjudge.net/problem/POJ-1170
题意:输入n,表示有那种物品,接下来n行,每行a,b,c三个变量,a表示物品种类,b是物品数量,c代表物品的单价。接下来一个数字m,表示有m个套餐,接下来m行,每行一个数字k,表示有k对数字,后面接k对数字,分别是物品种类和物品数量,最后一个value代表这个套餐的价格(套餐价格一定比单独买这些东西的价格低)。现在要我们求出买上面所有东西所需要的最低价格。
分析数据会发现物品种类会超过5,每种物品数量不会超过5,所以可以用状态压缩,虽然我第一发是用完全背包加六个循环...
思路就是这样,具体看代码:
状压dp:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 50005 /*struct point{ int u,w; }; bool operator <(const point &s1,const point &s2) { if(s1.w!=s2.w) return s1.w>s2.w; else return s1.u>s2.u; }*/ map<int,int>mp; int n,m,k,t,MAX; int num[10],value[10],vis[maxn];//vis标记i这个数字是不是超出了物品最大数量 int bit[10]; int dp[maxn]; struct node{ int state,value;//记录套餐的状态压缩的值和套餐价格 }item[105]; int check(int a)//检查a这个数字是不是每一种物品数量都不超出数量限制 { for(int i=0;i<6&&a;i++){ if(a%6>num[i]) return false; a/=6; } return true; } int cal(int a)//计算状态压缩值a在不考虑套餐情况下需要的花费 { int ans=0; for(int i=0;i<6&&a;i++){ ans+=(a%6)*value[i]; a/=6; } return ans; } void init()//把每一个状态在不考虑套餐是需要的花费1计算出来 { memset(dp,0,sizeof(dp)); for(int i=0;i<=MAX;i++){ if(check(i)) { vis[i]=1;//表示i是可行的 dp[i]=cal(i); } } } int main() { bit[0]=1; for(int i=1;i<=6;i++){ bit[i]=bit[i-1]*6; } while(scanf("%d",&n)!=EOF){ memset(num,0,sizeof(num)); memset(vis,0,sizeof(vis)); MAX=0; int id=0,a,b,c; mp.clear(); for(int i=1;i<=n;i++){ scanf("%d%d%d",&a,&b,&c); if(mp[a]==0) mp[a]=id++;//给物品编号 num[mp[a]]=b; value[mp[a]]=c; MAX+=b*bit[mp[a]]; } scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d",&k); int state=0; for(int j=1;j<=k;j++){ scanf("%d%d",&a,&b); state+=b*bit[mp[a]]; } item[i].state=state; scanf("%d",&item[i].value); } init(); for(int i=1;i<=m;i++){ for(int j=item[i].state;j<=MAX;j++){ if(vis[j]&&vis[j-item[i].state])//j和j-item[i].state都不超出限制 dp[j]=min(dp[j],dp[j-item[i].state]+item[i].value); } } printf("%d ",dp[MAX]); } return 0; }
六个循环的代码就不写了,傻乎乎啊。