2016 UESTC Training for Dynamic Programming F - 柱爷与三叉戟不得不说的故事 压位dp

来源:互联网 发布:dmx控制器淘宝 编辑:程序博客网 时间:2024/05/13 17:40

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

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

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

拥有毁天灭地的力量

但只有很少人知道当柱爷获得三叉戟时,三叉戟的力量已经消失殆尽.为了能修复三叉戟的力量,柱爷苦心专研

古籍,发现只有找到最神秘的15种元素,将元素的力量重新注入到三叉戟中,三叉戟就能恢复到原来的力量,

于是柱爷踏上了寻找神秘元素的旅行.

柱爷首先发现15个由卿学姐掌控的地牢,里面正好有15种对应元素.冷静的柱爷没有贸然行动,经过一番详细的打听,

柱爷得知了第i种元素需要Di的精力去获得;与此同时,柱爷还发现了由白学潘看守的N个洞穴,第i个洞穴中

有Ai个不同的元素,分别是B1,B2,...,B Ai,需要Ci的精力去获得.

同种元素不得拥有多个,不然元素之间会产生强大的斥力,会发生爆炸;不能攻下一个洞穴后,留有元素不拿走,

元素可能会辐射,伤害喵哈哈村的大爷......

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

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

Input

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

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

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

数据保证:

  • 0 <= N <= 10000

  • 1 <= Di <= 20000

  • 0 <= Ai <= 5

  • 1 <= Ci <= 2000000

Output

输出一个整数ans:

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

Sample input and output

Sample InputSample Output
9599 11186 3513 3112 19106 1171 4254 4786 11880 19550 4793 17049 10779 11096 971354 10 1 8 11 12126453 14 9 15 16555603 7 3 14 11612625 13 5 9 2 11 7105120 980331
141587

Source

2016 UESTC Training for Dynamic Programming


My Solution

压位dp

dp[i] 表示状态为i是的最小消费, 状态i则是i的二进制表示以后哪些地方是1表示有已经获得了哪些元素

Ind = 1<<0, 表示有元素 1;

Ind = 1<<15 表示有元素 15;

Ind = 0;

Ind += 1<<0;]

Ind += 1<<15;

然后Ind 表示有元素 1 、15;

然后有十五个元素, 所以最多1<<15, 设为 maxi = (1<<15) + 2;

然后 

for(int i = 0; i < maxi; i++){
        for(int j = i; j; j = (j - 1) & i){         //!这样就可以枚举了可能的子集了
            dp[i] = min(dp[i], dp[j] + dp[j^i]);
        }
 }

就好了,dp[maxn]先初始化为 Inf = 1e9数量级的, memset(dp, 0x3f, sizeof dp);

然后输入的时候根据元素集合计算Ind , 保留min{dp[Ind};

然后从0 ~ maxi 所以状态,包括无效状态(就是并没有值输入, 为Inf的那些状态)一起转移;

最好Ind = 0, i = 0 ~ 15

Ind += 1<<i;

然后输出dp[Ind].

复杂度 O(1e7);

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn = 50000, maxi = (1<<15) + 2;int dp[maxn];int main(){    #ifdef LOCAL    freopen("a.txt", "r", stdin);    #endif // LOCAL    int n, d, a, ai, c, Ind;    memset(dp, 0x3f, sizeof dp);    //cout<<(dp[0])<<endl;   1e9级的    //int l = 1e9;    //cout<<l<<endl;    for(int i = 0; i < 15; i++){        scanf("%d", &d);        Ind = 1<<i;        //cout<<Ind<<endl;        dp[Ind] = d;    }    scanf("%d", &n);    while(n--){        Ind = 0;        scanf("%d", &a);        for(int i = 0; i < a; i++){            scanf("%d", &ai);            Ind += (1<<(ai-1));  //!WA5    用 1<<0 表示有 元素 1 的        }        scanf("%d", &c);        if(Ind != 0) dp[Ind] = min(dp[Ind], c);     //WA5  不是test5 触发的Bug  但如果 a == 0, 就是什么也没有 好吧, 没什么影响有效的是1开始的    }    for(int i = 0; i < maxi; i++){        for(int j = i; j; j = (j - 1) & i){         //!这样就可以枚举了可能的子集了            dp[i] = min(dp[i], dp[j] + dp[j^i]);        }    }    Ind = 0;    for(int i = 0; i < 15; i++)        Ind += (1<<i);    printf("%d", dp[Ind]);    return 0;}




Thank you!


                                                                                                                                               ------from ProLights


0 0
原创粉丝点击