[Codeforces 585D] Lizard Era: Beginning (折半枚举)

Codeforces - 585D

有三个开始为 0的数,有 N 次选择,每次改变其中两个
问最后使得三个数相等且最大的选择方案
其中 N25


一看 N25 ,就可以暴力算一算
直接状压是不行的,所以折半状压
先用三进制压前一半,每一位表示不选的那个人
然后把后两个人对第一个人价值的差当作 key
存一下第一个人的最大价值,以及此时的状态
然后枚举后一半,就可以 log(3N2) 找出使三个人相等的最优合法状态
时间复杂度为 (3N2log(3N2))

如果将最后的前一半和后一半的状态搞在一起的话
要注意 325 爆 int这个问题 (调了一年orz)

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")

const char name[]="LMW";
const int INF=0x3f3f3f3f;
int N, W[3][30], pow3[15];
map<Pii,Pii> stor;

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    pow3[0]=1;
    for(int i=1; i<15; i++) pow3[i]=3*pow3[i-1];
    while(~scanf("%d", &N))
    {
        stor.clear();
        int hl=N>>1, le=N-hl, ans=-INF;
        LL mask=0;
        for(int i=0; i<N; i++) for(int j=0; j<3; j++) scanf("%d", &W[j][i]);
        if(N==1)
        {
            int cnt=0; char res[3]={0};
            for(int i=0; i<3 && cnt<2; i++) if(!W[i][0]) res[cnt++] = name[i];
            if(cnt<2) puts("Impossible");
            else puts(res);
            continue;
        }
        for(int m=0,res[3],tem; m<pow3[hl]; m++)
        {
            tem = m;
            CLR(res);
            for(int i=0; i<hl; i++)
            {
                for(int j=0; j<3; j++) if(j!=tem%3) res[j] += W[j][i];
                tem /= 3;
            }
            res[1] -= res[0]; res[2] -= res[0];
            auto it = stor.find(Pii(res[1],res[2]));
            if(it == stor.end()) stor.insert( make_pair(Pii(res[1],res[2]), Pii(res[0],m)) );
            else if(it->second.first < res[0]) it->second = Pii(res[0], m);
        }
        for(int m=0,res[3],tem; m<pow3[le]; m++)
        {
            tem = m;
            CLR(res);
            for(int i=hl; i<N; i++)
            {
                for(int j=0; j<3; j++) if(j!=tem%3) res[j] += W[j][i];
                tem /= 3;
            }
            res[1] -= res[0]; res[2] -= res[0];
            auto it = stor.find(Pii(-res[1], -res[2]));
            if(it != stor.end() && it->second.first+res[0] > ans)
            {
                ans = it->second.first+res[0];
                mask = (LL)m*pow3[hl] + it->second.second;
            }
        }
        if(ans==-INF) puts("Impossible");
        else
        {
            char res[3]={0};
            for(int i=0; i<N; i++,mask/=3,puts(res)) for(int j=0,cnt=0; j<3 && cnt<2; j++) if(j!=mask%3) res[cnt++] = name[j];
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值