CDOJ 1355 柱爷与三叉戟不得不说的故事(枚举子集状压dp)

7 篇文章 0 订阅
2 篇文章 0 订阅

柱爷与三叉戟不得不说的故事

Time Limit: 500/500MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

在喵蛤蛤城,人人都知晓柱爷有一把传奇武器三叉戟.据传,这是一位在 772002 772002年前陨落的强大守护者F91曾经使用过的12级灵能武器,拥有毁天灭地的力量

但只有很少人知道当柱爷获得三叉戟时,三叉戟的力量已经消失殆尽.为了能修复三叉戟的力量,柱爷苦心专研古籍,发现只有找到最神秘的 15 15种元素,将元素的力量重新注入到三叉戟中,三叉戟就能恢复到原来的力量,于是柱爷踏上了寻找神秘元素的旅行.

柱爷首先发现 15 15个由卿学姐掌控的地牢,里面正好有 15 15种对应元素.冷静的柱爷没有贸然行动,经过一番详细的打听,柱爷得知了第 i i种元素需要 Di Di的精力去获得;与此同时,柱爷还发现了由白学潘看守的 N N个洞穴,第 i i个洞穴中有 Ai Ai个不同的元素,分别是 B1,B2,...,BAi B1,B2,...,BAi,需要 Ci Ci的精力去获得.

同种元素不得拥有多个,不然元素之间会产生强大的斥力,会发生爆炸;不能攻下一个洞穴后,留有元素不拿走,元素可能会辐射,伤害喵哈哈村的大爷......

但是柱爷岂是凡人?柱爷修复三叉戟时花费的总是最小的精力!

那么这个最小的花费是多少呢?

Input

第一行包括 15 15个正整数,第 i i个数表示 Di Di.

第二行包括一个整数表示 N N.

接下来 N N行,每行有一个整数表示 Ai Ai,后面紧接着 Ai Ai个不同的正整数,最后又一个正整数 Ci Ci表示需要花费的精力.

数据保证:

  • 0N10000 0≤N≤10000

  • 1Di20000 1≤Di≤20000

  • 0Ai5 0≤Ai≤5

  • 1Ci2000000 1≤Ci≤2000000

Output

输出一个整数 ans ans:

表示至少花 ans ans的精力去修复三叉戟.

Sample input and output

Sample Input Sample Output
9599 11186 3513 3112 19106 1171 4254 4786 11880 19550 4793 17049 10779 11096 9713
5
4 10 1 8 11 1212645
3 14 9 15 1655560
3 7 3 14 1161262
5 13 5 9 2 11 710512
0 980331
141587

Source

2016 UESTC Training for Dynamic Programming

题解:这是一道状压dp题目,看到15种元素,可以想到状压到2^15次方显然是可以的,然后就是枚举子集找最小值了,一开始我手写了一个暴力枚举,但是T了,后来在一篇博客中看到了这种枚举方式for(int j=i;j;j=(j-1)&i),dp[i]=min(dp[i],dp[j]+dp[j^i]);改了之后就过了。

代码如下:

#include <iostream>
#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
LL dp[66000];
LL bit1[20];
LL b15;


int main()
{
    LL b15=2;
    bit1[1]=2;
    bit1[0]=1;
    fill(dp,dp+66000,1000000000000);
    for(int i=2;i<=15;i++){
        bit1[i]=bit1[i-1]*2;
        b15+=bit1[i];
    }
    for(int i=1;i<=15;i++)
        scanf("%lld",&dp[bit1[i]]);
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int nod;
        scanf("%d",&nod);
        LL nn=0;

        for(int j=0;j<nod;j++)
        {
            //cout<<1<<endl;
            LL val;
            scanf("%lld",&val);
            nn+=bit1[val];
        }
        //cout<<1<<endl;
        LL vl;
        scanf("%lld",&vl);
        dp[nn]=min(dp[nn],vl);
        //cout<<dp[nn]<<endl;
    }
    for(int i=1;i<=b15;i++)
    for(int j=i;j;j=(j-1)&i)
    {
       dp[i]=min(dp[i],dp[j]+dp[j^i]);
        //cout<<dp[i]<<endl;
    }
    cout<<dp[b15]<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值