POI1998 ATM 自动取款机

Description

在一个银行的大厅里有30个自动柜员机,编号分别为0..29,每个会员顾客都能通过这些柜员机提取10^9 ducats(一种早期的流通硬币单位)的借款业务,但必须在一个星期内还是通过这些柜员机完成还款业务。这种柜员机很特别,每个柜员机只能完成一种简单的动作:供客户提取固定数目的现金或接受客户固定数目的还款。第i个柜员机只能提供2^i ducats的借款业务,如果i是偶数的话;而如果i是奇数的话,则第i个柜员机只能提供2^i ducats的还款业务。
现在,如果有一个客户想要用这些柜员机借一定数目的钱,但是每个柜员机都只能使用一次,那么,他该使用哪些柜员机呢?
一个星期内还款时,他又该使用哪些柜员机呢?
比如,他要借7 ducats,则他会从4号柜员机拿到16 ducats,从0号柜员机拿到1 ducats,再到3号柜员机还掉8 ducats,到1号柜员机还掉2 ducats。
一个星期内还款时,他会到3号柜员机先还掉8 ducats,再到0号柜员机拿到1 ducats。
请你编写一个程序,当一个客户借款,告诉他该使用哪些柜员机,以后还款时又该使用哪些柜员机。

Input

第一行为整数N(N<=1000),表示客户的数目。下面有N行,每行为一个正整数,表示每个客户要借多少钱。注意:每个客户最多能借10^9 ducats。

Output

共2N行,第2i-1行表示客户i借款时该使用哪些柜员机,第2i行表示客户i还款时该使用哪些柜员机(1<=i<=N),每行都要求按柜员机编号从大到小输出,每个编号之间用1个空格隔开。
如果业务无法成交,则输出NIE。

Sample Input

2
7
385171980

Sample Output

4 3 1 0
3 0
NIE

29 28 27 24 20 19 18 17 16 15 14 9 5 4 2


    这是今天做到的非常有趣的一道题目,方法很多。首先我们肯定会联想到普通二进制的运算,因为普通的二进制可以由某一位一下子确定下来,比如2^4次 不会由其他位上的选择影响到,但是这道题目当中2^4可能是2^5-2^4,所以一下子比较难想出来。

    第一种方法,从低位开始看,如果总数模2为奇数,那么后面的数不论怎么搞处理出来必定是2的倍数,那么我们就可以说不管这位是加还是减,那么我们肯定要选的,否则这位现在不处理掉,将来肯定处理不掉,这样就抛弃了正负的影响,可以直接得出结果。


#include<stdio.h>
#include<iostream>

using namespace std;

int ans[40],ji;

void Ans(){
    cout << ans[ji-1];
    for(int i = ji-2; i >= 0;i--)
        cout << " " << ans[i];
    cout << endl;
}
void cal(int n){
    ji = 0;
    for(int i = 0; i < 30;i++) {
        int tem = n%2;
        if(0 != tem) {
            ans[ji] = i;
            ji ++;
            if(0 == i%2)
                n++;
            else
                n--;
        }
        n /= 2;
    }
    if(0 == n)
        Ans();
    else
        cout << "NIE" << endl;
}

int main(){
    int cas;
    int  n;
    cin >> cas;
    for(int i = 0 ;i < cas;i ++) {
        cin >> n;
        cal(-n);
        cal(n);
    }
    return 0;
}

第二种方法 非常巧妙,假设我们要得到X元,那么我们就先将左右都能得到的都先拿到,假设得到了Y元,那么我们就要舍弃Y-X元,那么现在仅有一种选择,就是在不用的位上退掉多少钱,那么就是只要将这个数字直接化成一个二进制表示就可以了 代表了选择情况。


第三种情况,搜索状态,有30种物品,那么我们总共的状态数有2^30之多,如果去搜索每个状态肯定是不行的,我们这里就考虑到每出现一个物品,总状态数就会呈指数增长,所以我们这里将所有状态分成两堆,每对15个, 2^15状态,对于第一堆中的每个状态,在第二个状态中做二分搜索,或者直接开哈希表哈希。就是15*2^15状态,很小。

引用风神的话说就是这样的。

二分状态搜索

        多半应用于背包的搜索,就是给你n<=30个物品,每个物品有一定的价值(Value<=10^9),问你将其分成两堆,使得两堆的总价值和差值最小是多少.

        如果我们直接dfs搜索的话,复杂度会达到O(2^n).极限复杂度就是 2^30 !!!

        我们可以先记录sum为所有物品的价值总和,然后先将前15个物品的所有组合状态用一个hash[state]记录下来,然后按价值排序.

        再对后15个物品枚举组合状态,记其组合的价值为Val,那么我们在hash[]中二分查找一个状态state,使得Val+hash[state]最接近sum/2(就是总价值的一半)...然后最小差值就在 O( (2^15) * log2(2^15) ) + O( (2^15)*log2(2^15) )  [前15个状态排序+后15个状态二分的复杂度] 的时间内完美解决了...


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值