• [bzoj 2460]线性基+贪心+证明过程


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2460

    网上很多题目都没说这个题目的证明,只说了贪心策略,我比较愚钝,在大神眼里的显然的策略还是想证明一下才安心……所以这里记录一下证明过程。

    贪心策略:按魔力值从大到小排序,从大往小往线性基里插,如果成功插入新元素,就选这个,如果插不进去,就不选这个。

    证明:

      设有n个材料,每个材料的属性值是x[1],x[2],...,x[n],魔力值是v[1],v[2],...,v[n],这里假设v已经排好序,即v[1]>=v[2]>=v[3]>=...>=v[n]。

      首先证,一定有一个最优解,包含材料1,其属性值是x[1],魔力值是v[1]。

        假设原问题存在一个最优解 S = { t1, t2, ... , tk }。其中ti代表第ti个物品,且t1<t2<...<tk。

          如果t1等于1,那么得证。

          如果t1不等于1,那么我们来证一定有一个元素可以被1替换下来。

            考虑1为何不能加进S。因为S是线性无关的,加入1以后,S∪{1}就变得线性相关了。所以必然存在S的一个子集,它们的异或和等于x[1]。

            用表达式写出来也就是

    (1)

            那么1可以把谁替换下来呢?答案是1可以把任何一个替换下来。我们不妨让它替换下来ti,把式子变一下形,两边同时异或上x[ti]^x[1],就得到了

    (2)

            就会发现x[ti]已经可以被线性表示出来了,而且显然,如果不加x[1]肯定是无法线性表示出来x[ti]的(因为S是线性无关的),所以替换后的线性基跟原来是等价的。

            如果不放心,我可以再重述一遍,对于原来S可以表示出来的,替换后的一定也可以表示出来,因为被替换掉的x[ti]已经可以表示出来了;对于原来S不能表示出来的,替换后的也一定表示不出来。可以用反证法证。假设有一个y,用原来的表示不出来,而用替换后的可以表示出来。那肯定是因为加入了x[1]的原因。用式子写出来就是:

    (3)

            把x[1]用(1)式代换,就可以得到:

    (4)

            是不是担心,万一左边的x都抵消没了怎么办?实际上不会出现这种情况,因为ti就是独一无二的,在x[i]^...^x[j]里是不会有的(因为ti已经被1替换下来了)。这样,就得到了原来的基也可以得到y,与假设矛盾。

      所以这一步证明的作用是什么呢?就是证明了,第一步的贪心策略是正确的。下面来证明,如果第一步的贪心是正确的,以后的贪心也是正确的。

      现在只需证,假设当前已经按照贪心策略造出了一个线性无关的基S = { t1, t2, ... , tk },一定存在一个最优解,包含下一步选择的那个最大魔力值的跟S线性无关的一个材料。

      设下一步的贪心策略选择是j,假设最优解是 G = {t1, t2, ... , tk , tk+1, tk+2, ... , tk+m}。

        如果j∈G,那么得证。

        如果j∉G,现在证j一定可以替换掉G中的某个元素,实际上j可以替换掉跟它线性相关的那些元素里的任何一个元素,证明方法跟第一步类似。

          j为什么不能属于G呢?因为G是线性无关的,但是加入j之后,就线性相关了,也就是说j是多余的,j可以用其他的线性表示出来。那么可以得到的式子就是:

    (5)

    (补充:图片里的i, j, k都是代指任意变量)

        这个式子实际上跟(1)式是一模一样的。而且这里的i肯定>k,因为根据已知的策略,j一定会选跟t1...tk线性无关的最前面的那个。那么到此,后面的证明跟第1步的证明也是类似的,j也可以替换掉任何一个ti (i>k)。

      综上,问题得证。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int maxn=1005;
    pair<int,ll> a[maxn];
    
    vector<ll> base;
    bool add(ll x)
    {
        for(int i=0;i<base.size();i++)
            x=min(x,x^base[i]);
        if (x) base.push_back(x);
        if (x) return true;
        else return false;
    }
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for (int i=0;i<n;i++) scanf("%lld%d",&a[i].second,&a[i].first);
        sort(a,a+n);
        int ans=0;
        for (int i=n-1;i>=0;i--) if (add(a[i].second)) ans+=a[i].first;
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    linux,windows kettle安装方法
    等待事件分类
    分析函数详细例子
    v$session中不同连接方式module,program的区别
    charles Glist发布设置
    charles 发布Glist
    charles 工具菜单总结
    charles 高级批量请求
    charles 批量重复请求/重复发包工具
    charles 重写工具/rewrite Srttings
  • 原文地址:https://www.cnblogs.com/acmsong/p/7508022.html
Copyright © 2020-2023  润新知