bzoj3900交换茸角

本文介绍了一种通过交换麋鹿的茸角以确保其重量差不超过特定阈值的方法。通过状态压缩和动态规划实现了最小交换次数的有效计算。

3900: 交换茸角

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 236   Solved: 104
[ Submit][ Status][ Discuss]

Description

动物园里有 n 头麋鹿。每头麋鹿有两支茸角,每支茸角有一个重量。然而,一旦某头麋鹿上
两支茸角的重量之差过大,这头麋鹿就会失去平衡摔倒。为了不然这种悲剧发生,动物园院长决
定交换某些茸角,使得任意一头麋鹿的两角重量差不超过 c。然而,交换两支茸角十分麻烦,不
仅因为茸角需要多个人来搬运,而且会给麋鹿造成痛苦。因此,你需要计算出最少交换次数,使
得任意一头麋鹿的两角重量差不超过 c。
注意,交换两支茸角只能在两头麋鹿之间进行。因为交换同一头麋鹿的两支角是没有意义的。

Input

第一行为整数 n,c。接下来 n 行,每行两个整数,分别表示一开始每头麋鹿的两角重量。

Output

一个数,即最少交换次数。如果无论如何也不能使每头麋鹿平衡,输出 -1。

Sample Input

3 0
3 3
2 5
2 5

Sample Output

1

HINT

对于 100% 的数据,n <= 16, c <= 1000000, 每支茸角重量不超过 1000000。



Source


首先看一下这个:http://blog.youkuaiyun.com/lvzelong2014/article/details/78897057

这题其实和bzoj2064方法基本一致。判断交换麋鹿可行性就是排个序然后相邻小于等于c即可。

然后对于一个合法状态最多只要交换n-1次,因为如果很傻地一个一个配对,前面配对n-1之后最后一对就配对完了。

然后就是和bzoj2064的状压一样了,只不过这题中单个1的状态可能是合法的,必须枚举子集。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 - '0' + ch; ch = getchar();}
    return x * f;
}
int bin[20], a[20], b[20], h[40], f[100000], n, c, top;
 
int judge(int s) {
    top = 0;
    for(int i = 0;bin[i] <= s; ++i) if(bin[i] & s) h[++top] = a[i], h[++top] = b[i];
    sort(h + 1, h + top + 1); 
    for(int i = 1;i < top; i += 2)
    if(h[i + 1] - h[i] > c) return -1;
    return 1;
}
 
int main() {
    bin[0] = 1; for(int i = 1;i < 20; ++i) bin[i] = bin[i - 1] << 1;
    n = read(); c = read();
    for(int i = 0;i < n; ++i) a[i] = read(), b[i] = read();
    if(!(~(f[bin[n] - 1] = judge(bin[n] - 1)))) {puts("-1"); return 0;}
    for(int s = 1;s < bin[n] - 1; ++s) f[s] = judge(s);
    for(int s = 1;s < bin[n]; ++s)
    if(~f[s])
        for(int t = (s - 1) & s; t; t = (t - 1) & s)
        if(~f[t] && ~f[s ^ t])
            f[s] = max(f[s], f[s ^ t] + f[t]);
    printf("%d\n", n - f[bin[n] - 1]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值