題解/算法 {G - Cut and Reorder}

題解/算法 {G - Cut and Reorder}

@LINK: https://atcoder.jp/contests/abc328/tasks/abc328_g;

先對A進行一次操作(比如A=[a,b | c,d | e,f] 切2刀(代價是C*2) 然後把他亂序變成[ef, cd, ab])
然後再累加abs(A[i]-B[i]);

此時有個性質 比如你切了K刀 1 | 2 | ... | K+1 然後亂序, 變成A1 | ... | A(K+1), 此時一定有Ai + 1 != A(i+1);
. 比如 1(ab) | 2(c) | 3(de) | 4(f), 如果你把他變成1 | 2 | 4 | 3 那麼你合併要1 | 2多切一刀呢, 他可以去掉;

因此, 假如你操作過後 A是3 4 1 2 0 那麼一定說明 原來切的是3 4 | 1 2 | 0 即一段連續遞增的子段裡面 一定沒有切;

因此, 第一個做法: 暴力枚舉N!全排列; 當然這會超時;

然後第二做法: 對N!全排列的優化 這是狀壓DP經過可以處理的, 此時優化為(1<<N)*N*N; (注意代碼中是abs(A[ind] - B[len-1]) 而不是B[ind]);
可是int64 [1<<22][22] 他需要4e6*20*8 = 6e8 Byte 已經爆空間了! 512MB = 5e8;

for( int st = 1; st < (1 << ___N); st ++){
    int len = __builtin_popcount( st); // `st`所表示的序列長度;
    //>< 比如`st`裡的`1`的下標有`{0,2,3,5}` 即`len=4`, 這個`st`表示`[0235,0253,0325,2...,3...,5...]`共`len!`個序列;
    if( len == 1){
        int ind = __builtin_ctz(st); // `st`中的`1`的下標
        ___DP[ st][ ind] = std::abs( A[ind]-B[len-1]);
        continue;
    }
    //>< `__builtin_popcount( st) > 1`;
    for( auto temp = st; temp > 0; temp-=(temp&-temp)){
        int curInd = __builtin_ctz( temp); // `curInd`從小到大枚舉`st`裡的所有的`1` (比如`st=11010` 則`ind=[1,3,4]`);
        auto & curDP = ___DP[ st][ curInd];
        bool __isFirst = true; // 比如用`[a,b,c,d]`更新`curDP`, 那麼第一次是`curDP=a` 後來是`Min/Max(curDP,b/c/d)`, 這個變量就用於此作用;

        auto preST = st ^ (1<<curInd);
        for( auto ttemp = preST; ttemp > 0; ttemp-=(ttemp&-ttemp)){
            int preInd = __builtin_ctz( ttemp); // `preInd`從小到大枚舉`preST`裡的所有的`1` (比如`preST=11000` 則`ind=[3,4]`);
            const auto & preDP = ___DP[ preST][ preInd]; // 此时表示所有的`[..., preInd, curInd]`序列 其中`[..., preInd] = preDP`;
            //>< 此時`preDP=[...,preInd]`已經確定了, 此時你要做的是 在他右側再添加一個`curInd` 即變成`[...,preInd,curInd]` 因此你要考慮這個操作的代價;
            //   . 比如`st`裡的`1`的下標有`{0,2,3,5}`(他的全排列有`4!`種方案), 比如`curInd=3, preInd=0` 那麼此時`curDP= [{025},3](共`3!`種方案)` `preDP= [{2,5},0,3](共`2!`種方案)`;

            auto updateDP = preDP;
            updateDP += std::abs( A[curInd] - B[len-1]);
            if( preInd+1 != curInd){ updateDP += C;}

            if( __isFirst){ curDP = updateDP; __isFirst=false;}
            else{ curDP = std::min( curDP, updateDP);}
        }
    }
}

正確做法 好像是對(1<<22)(22) 優化為(1<<22), 目前還沒有研究明白;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值