原题大意
有r种菜谱,有p种原料,每种原料都有相应的价格,已经有了m种原料,需要你做出n道菜,问最少需要花多少钱,注意:只要买了一种原料,下面如果还用到无需再买。输入的菜谱中,1表示需要这种原料,0表示不需要。
算法分析
刚开始,我想通过暴力+回溯,可是p的最大值可以取到13,结果,就超时了。后来看了网上的题解,算法应该这样设计:1.将菜谱当成2进制,然后用数组保存,因为最多有2^13种可能,数组开到9000就够了。2.用动态规划,每种菜谱要么选,要么不选,选了之后会对下面的选择造成影响,可以用记忆化搜索完成。
程序代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int INFI = (1 << 20) - 1;
int r;// 菜谱数
int p;// 配料数
int m;//已有的配料数;
int n;//需要做出来的菜的数量
int cost[15];//每种原料的价格
vector<int> ing_have;//已经有的原料序号
int dp[35][1 << 13][35];//dp[i][j][k],i表示第n道菜,j表示第n道菜的菜谱十进制表示,k表示已经选了k道菜时的最优解
int ing[35];//保存二进制菜谱的十进制数值
int Bin_to_Dec(int *into_int){//二进制转化为十进制
int i, j;
int ans = 0;
for(i = 0, j = p - 1; j >= 0; j--,i++)
{
ans += into_int[j] *pow(2,i);
}
return ans;
}
int calculate_cost(int cur, int num){//cur表示已经买过的原料,num表示该菜谱所需的原料
int Cost = 0;
for(int i = 0; i < p; i++)
{
int x,y;
x = cur % 2;
y = num % 2;
if(y == 1 && x == 0) //将cur,num再转化为二进制,判断哪些需要买,哪些已经买过
Cost += cost[p - i - 1];
cur /= 2;
num /= 2;
}
return Cost;
}
int solve(int idx, int cur, int cnt){
if(cnt == n ) return 0;
if(idx >= r) return INFI;
int &ans = dp[idx][cur][cnt];
if(ans != -1) return ans;
int res = INFI;
//做这道菜
int incl = calculate_cost(cur, ing[idx]) + solve(idx + 1, (cur|ing[idx]),cnt+1);
//按位或的运用
res = min(incl, res);
//不做这道菜
int excl = solve(idx+1, cur, cnt);
res = min(res, excl);
ans = res;
return ans;
}
int main(){
//freopen("input.txt", "r", stdin);
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d%d",&r, &p, &n, &m);
ing_have.clear();
int temp;
for(int i = 0; i < m; i++)
{ scanf("%d", &temp);ing_have.push_back(temp);}
for(int i = 0; i < p; i++)
scanf("%d", &cost[i]);
for(int i = 0; i < 35; i++)
for(int j = 0; j < (1 << 13); j++)
for(int k = 0; k < 35; k++)
dp[i][j][k] = -1;
int into_int[15];//模拟一道菜的菜谱
for(int i = 0; i < r; i++)
{
for(int j = 0; j < p; j++)
scanf("%d", &into_int[j]);
ing[i] = Bin_to_Dec(into_int);
}
for(int i = 0; i < 15; i++)
into_int[i] = 0;
for(int i = 0; i < ing_have.size(); i++)
into_int[ing_have[i]] = 1;
printf("%d\n", solve(0, Bin_to_Dec(into_int), 0));
}
return 0;
}