hdu 6140 Hybrid Crystals 阅读题 OR bitset 优化01背包

116 篇文章 0 订阅
24 篇文章 1 订阅

题目连接


题意:

确实比较坑,题中给出的信息保证了凑出的数连续,


但是我一直以为没什么用也没发现这个....


update:

http://blog.csdn.net/howardemily/article/details/77367390


做完这两个题我突然感觉对这个题目为什么一定保证是连续的有了新的感觉啊,并不知道自己理解的对否. 假设我们先只考虑L 的情况,那么所有的N也都看成L的话,那么当前可以凑出的最大的和为 sum(ai) (bi==N||bi==L) 那么此时新加入进来的ai 满足了 ai<=sum 的情况,根据上面我做的两题 只要满足 sum+1>=ai 即可证明是连续的,所以这里的话如果全为正数那么凑出来的一定是一个连续的区间范围. 那么全为正数区间右端点就是可以凑出做大的k.

只考虑负数的情况也同理,那么从而可以确定了能凑出的区间范围,直接判断k在里面就好了/


然后就发现01背包肯定T了啊,那我就bitset优化一下吧.第一次写bitset,没办法 xjb搞搞吧,然后就一直T.

这里bitset优化的时间复杂度大致是n*k/64  或者/32 吧 这个不太清楚.

如果这个是个搜索我肯定就想到了这种剪枝...但是没想到...这样优化的常数是巨大的.

就是当所有负数加起来的和 大于k,或者所有正数加起来的和小于k.那么肯定就凑不出来了啊...这时候就不需要去跑背包了,节省了常数...

另外我把所有N,都double了一下...这种方法不太提倡吧,毕竟不是正解。。。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <bitset>
using namespace std;
const int maxn = 2e6 + 7 ;
const int maxm = 2e3 + 7;
int a[maxm], num[maxm];
bitset<maxn> dp;
char b;

inline int read(){
    int x=0,f=1;char ch = getchar();
    for(;ch<'0'||'9'<ch;ch=getchar())if(ch=='-') f=-1;
    for(;'0'<=ch&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}


int main()
{
    int n, k, t;
    scanf("%d", &t);
    while(t--)
    {
        dp.reset();
        scanf("%d%d", &n, &k);
        // k += maxm;
        for(int i = 1; i <= n; i++)
            a[i] = read();
        int tot = 1;
        for(int i = 1; i <= n; i++)
        {
            scanf(" %c", &b);
            if(b == 'L')
                num[tot++] = a[i];// num[tot-1] += maxm;
            else if(b == 'D')
                num[tot++] = -a[i];//num[tot-1] += maxm;
            else
            {
                num[tot++] = a[i];// num[tot-1] += maxm;
                num[tot++] = -a[i];//num[tot-1] += maxm;
            }
        }
        int sum1=0,sum2=0;  
        for(int i=1;i < tot;++i){  
            if(num[i] < 0) sum2 += num[i];  
            if(num[i] > 0) sum1 += num[i];  
        }  
        
        
        
        if(sum2>k||sum1<k) //最最重要的剪枝 
        {
        	puts("no");
        	continue;
        }
		k += 1e6;
		dp.set(1e6);
        for(int i = 1; i < tot; i++)
        {
            if(dp.test(k))//一点小优化, 无影响. 
            break;
            if(num[i] > 0)
            {
                dp |= (dp << num[i]);
            }
            else
            {
                num[i] = -num[i];
                dp |= (dp >> num[i]);
            }
        }

        //k += 1e6;
        if(dp.test(k))
            puts("yes");
        else
        {
            puts("no");
        }
    }
    return 0;
}



知道是连续的好简单啊,以后我算是知道了,超过200+人过的都是水题,我大胆的猜结论蒙吧...还sb带着队友想优化 哈哈

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e3+10;
int a[maxn];
char b[maxn][2];
int n,k;
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		cin >> n >> k;
		for(int i = 1;i <= n;i++)
		scanf("%d",&a[i]);
		for(int i = 1;i <= n;i++)
		scanf("%s",b[i]);
		int l = 0,r = 0;
		for(int i = 1;i <= n;i++)
		{
			if(b[i][0] != 'L')
			l -= a[i];
			if(b[i][0] != 'D')
			r += a[i];
		}
		if(l <= k && k <= r)
		puts("yes");
		else
		puts("no");
	}
	return 0;
} 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值