卡牌游戏II

来源:互联网 发布:仙界网络直播间80txt 编辑:程序博客网 时间:2024/05/13 21:26

题目链接:http://acm.tongji.edu.cn/problem.php?id=1093

题目描述

有N张卡片,上面印着整数a1,a2,a3……an,
可以选取其中任意数量张卡片,求出一个和S,共有2^N-1个和
要求出最小的前N个和分别是多少

输入格式

题目包含多组数据。
输入的每一行有一个整数T(1<=T<=5)代表有T组数据
对于每组数据分为两行:
第一行有两个整数N,K(其中1<=N<=200000,1<=K<=min(2^N-1,200000))
第二行有N个整数,分别是a1,a2,a3……an(-10^8<=ai<=10^8)

输出格式

对于每组数据,输出的第一行为Case #x:,其中x是数据编号(从1开始)
第二行到第K行,每行一个整数,从小到大。
第二行输出一个非负整数,为正常需要输出的K的数字的和 Mod 99989的结果。

样例

样例输入:

2
2 1
1 1
3 3
-1 0 1

样例输出:

Case #1:
1
Case #2:
99987

题解:

因为要求的是最小的值,所以最小值一定是取所有的负数,且不取任何整数的情况。
又因为少取一个负数就相当于多拿一个绝对值相等的正数,因此就可以把负数的和记录下来,并将负数转化为正数进行处理。
下一步就是从N个正数当中取数,使得和从小到大排列的问题了,这里可以使用优先队列的bfs进行处理。
记得负数转化完之后,全取和全不取的情况会发生变化,要单独考虑。

这里的bfs用的是优化过的方式。正常的bfs是处理完第i张的情况后,将第i+1~n张卡分别加入并推入队列,时间复杂度为O(N^2*logN/2),太高了。
因此这里使用标记next,每次bfs的时候除了加入新卡外,额外进行一次“回溯”操作,将不取第next张卡而取第next+1张卡的情况加入队列。因为卡片是从小到大排列的,因此这种操作既保证了小数更早处理,也大大减少了bfs的操作数(在k远小于N^2的情况下)。

最终时间复杂度为O(klogN)。

程序:

#include<iostream>#include<string>#include<cstdio>#include<set>#include<map>#include<stack>#include<list>#include<vector>#include<queue>#include<algorithm>#include<cstring>#include<cmath>#include<fstream>using namespace std;typedef long long ll;const int M = 99989;ll n,k,sum,min_,ans;vector<ll> card;void bfs(){    priority_queue<pair<ll,ll>,vector<pair<ll,ll>>,greater<pair<ll,ll>> > QW;//存放当前的和Val以及下一张可以取的卡编号next    QW.push(make_pair(0,0));    sum = 0;    bool sp = true;    while(sum < k && !QW.empty()){        ll val = QW.top().first,next = QW.top().second;QW.pop();        if (next < card.size() && !(val == 0 && next == 0))//如果还有能取的卡            QW.push(make_pair(val-card[next-1]+card[next], next+1));//将不取当前卡片,而是取下一张的情况推入队列        if (val == -min_ && sp){//全不取的特殊情况            sp = false;            sum--;        }        ans = (ans + min_ + val) % M;        sum++;        if (next < card.size())            QW.push(make_pair(val+card[next], next+1));//取下一张卡    }}void init(){    cin >> n >> k;    int t;    min_ = 0;    ans = 0;    card.clear();    for(int i = 0;i < n;++i){        cin >> t;        {            if (t < 0){                min_ += t;//保存负数的和                t = -t;            }            card.push_back(t);        }    }    sort(card.begin(),card.end());}int main(){    int t;    cin >> t;    for (int k = 1;k <= t;++k){        init();        printf("Case #%d: \n",k);        bfs();        if (ans < 0) ans += M;        cout << ans << endl;    }}
0 0