Bottle Taps

46 篇文章 1 订阅

1326. Bottle Taps

Time limit: 3.0 second
Memory limit: 64 MB
Programmer Petrov has a hobby to collect beer-bottle taps. There’s nothing unusual — he knows hundreds of programmers that like beer. And they collect taps, too. Not everyone, but some of them.
Frankly speaking, he has bought a part of his collection. But unfortunately he hasn’t got some rare taps to complete his collection. He has found some programmers over the Internet that are ready to sell him these taps. Some of the programmers sell the taps in sets with big discounts.
It’s left to find an optimal offer. Petrov can explain to his wife why he is to store the taps but he won’t be able to prove why he is to spend money for the collection. So he is to buy the taps as cheap as possible.
Petrov has written down all the variants and has started thinking. There’s no way to find out the solution of the problem without a program!

Input

The first line contains an integer  N, an amount of available taps  (1 ≤  N ≤ 20) . The following  Nlines contain prices of bottles with the taps if one buys them in stores. The next line contains an integer  M  (0 ≤  M ≤ 100)  — an amount of offers to sell the taps. The following  M lines describe the sets. The first number of each line is the price of the set and the second one is the amount of taps in the set. Then there are numbers of the taps in the set (each number lies in the range from 1 to  N). The numbers in a set are unique. All the prices are positive integers and do not exceed 1000. The last line begins with the amount of taps that Petrov plans to buy. Then their numbers follow separated by spaces. These numbers are unique, too.

Output

Output the minimal sum of money that Petrov should spend on obtaining the necessary taps.

Sample

input output
4
10
11
12
13
3
17 2 1 3
25 3 2 3 4
15 2 3 4
3 1 3 4
25

/**
   题意:收集瓶盖,首先给出单个收集时,每种瓶盖的价格;
   然后,给出m种优惠策略,
   再给出收集目标,求最小花费。
   注意:
   比不要恰好收集齐目标,可以收集的比目标多,但收集目标必须完成;
   每种瓶盖也不一定只收集一个,可以多个;
**/
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <vector>
#include <bitset>
#include <cstdio>
#include <string>
#include <numeric>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long  ll;
typedef unsigned long long ull;

int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};//up down left right
bool inmap(int x,int y,int n,int m){if(x<1||x>n||y<1||y>m)return false;return true;}
int hashmap(int x,int y,int m){return (x-1)*m+y;}

#define eps 1e-8
#define inf 0x7fffffff
#define debug puts("BUG")
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define read freopen("in.txt","r",stdin)
#define write freopen("out.txt","w",stdout)
#define maxn 1<<20
/*
http://acm.timus.ru/problem.aspx?space=1&num=1326
*/
struct Node
{
    int w;
    int s;
}node[maxn];

int a[22];
long long dp[maxn];
int n;
int m;
int S;

/*每种至多买一件的价钱*/
void dfs(int s0,int x,int w)//当前状态s0,当前物品为x,当前状态的价格为w
{
    if(x==n)//表示前面n件已经遍历完了
    {
        dp[s0]=w;
        return ;
    }
    dfs(s0,x+1,w);            //表示没买第x件
    dfs(s0|(1<<x),x+1,w+a[x]);//表示买了第x件后,的价值为p+a[x]
}
/*每种至多买一件的价钱*/
void gao()
{
    int total=(1<<n)-1;
    for(int i=0;i<=total;i++)//easy to understand.
        for(int j=0;j<n;j++)
            if((1<<j)&i)
                dp[i]+=a[j];
}

void DP()
{
    int total=(1<<n)-1;
    long long  ans=inf;
    //dfs(0,0,0);
    gao();//初始化dp,dp初始值是当前状态没有任何优惠时的价值
    for(int i=0;i<=total;i++)
    {
        if((i&S)==S)//因为只要买的状态包含题目要求的状态,就可以算作是答案
            ans=min(ans,dp[i]);
        for(int j=0;j<m;j++)
            dp[i|(node[j].s)]=min(dp[i|(node[j].s)],dp[i]+node[j].w);//因为有优惠,将优惠之后得到的状态的dp进行更新
    }

    printf("%lld\n",ans);
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);

    scanf("%d",&m);
    for(int i=0;i<m;i++)//保存m种优惠
    {
        int w,j,s0=0;
        scanf("%d%d",&w,&j);
        for(int k=0;k<j;k++)
        {
            int x;
            scanf("%d",&x);
            s0+=(1<<(x-1));
        }
        node[i].w=w;
        node[i].s=s0;
    }

    int j;
    scanf("%d",&j);//用于确定目标状态
    for(int k=0;k<j;k++)
    {
        int x;
        scanf("%d",&x);
        S+=(1<<(x-1));
    }

    DP();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值