zoukankan      html  css  js  c++  java
  • 洛谷 P4660 & bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈

    题目:https://www.luogu.org/record/show?rid=12702916

    https://www.lydsy.com/JudgeOnline/problem.php?id=1168

    一眼不可做...即使数据范围很小...

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=25;
    int n;
    ll a[xn],b[xn],inf=1e17;
    bool cmp(int x,int y){return b[x]>b[y];}
    int main()
    {
      scanf("%d",&n);
      for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
      for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
      ll ansa=0,ansb=0; int tot=0;
      for(int i=1;i<=n;i++)
        {
          if(a[i]&&!b[i])ansa+=a[i];
          else if(!a[i]&&b[i])ansb+=b[i];
          else if(a[i]&&b[i])a[++tot]=a[i],b[tot]=b[i];
        }
      int mx=(1<<tot); ll pa=inf,pb=inf;
      for(int s=0;s<mx;s++)
        {
          ll tmpa=0,tmpb=0,mxa=inf,mxb=inf; int cnta=0;
          for(int i=1;i<=tot;i++)
        {
          if(s&(1<<(i-1)))tmpa+=a[i],mxa=min(mxa,a[i]-1),cnta++;
          else tmpb+=b[i],mxb=min(mxb,b[i]-1);
        }
          if(cnta==tot)tmpa-=mxa; if(cnta==0)tmpb-=mxb;
          if(!cnta)tmpa++; else tmpb++;
    //      if(cnta==tot)tmpa-=mxa,tmpb++;
    //      else if(cnta==0)tmpb-=mxb,tmpa++;
    //      else tmpa-=mxa,tmpb++;
          if(tmpa+tmpb<pa+pb)pa=tmpa,pb=tmpb;
        }
      printf("%lld
    %lld
    ",pa+ansa,pb+ansb);
      return 0;
    }
    还写过10分的错误思路

    首先,设每种颜色左右手套个数为 l[i],r[i],如果有 l[i]=0 或 r[i]=0,显然最劣情况要选,所以提前处理好;

    设一个状态 ans(L),表示左手套取 L 个时的答案;

    显然,这个答案是唯一的而且最劣的,现在要找到这个答案;

    取 L 个左手套,根据取法的不同,可以取出许多种颜色集合,把每一个可能的集合记作 T;

    而这些颜色集合又组成了一个集合,记作 A(L),表示选 L 个左手套会产生的颜色集合的集合;

    考虑每个集合 T,在右边都对应一种最劣的选法,就是把不在 T 中的颜色都选了,最后再+1;

    设这个为 R(T),即 R(T) = (∑r[i]) + 1,其中 i 不在集合 T 中;

    所以,ans(L) = max{ R(T) },其中 T 是 A(L) 中的一个集合,这样做很妙地没有限制什么而找到了最劣答案,保证了正确性;

    现在,考虑 A(L) 中有哪些 T,发现:

    A(L) = { T | |T| <= L <= S(T) },其中 |T| 是 T 这个集合包含的颜色数量,S(T) 是 T 这个集合包含的那些颜色的手套个数的和;

    由于我们找的是 max{ R(T) },而 |T| 越小,R(T) 越大,所以最终 ans(L) 的取值会是合法范围内某个 |T| 最小的,所以可以暂时忽略 |T| <= L 这个条件!!

    所以,现在就变成若 S(T) >= L,则 R(T) 可以更新 ans(L);

    然后,可以发现,会成为最终答案的 L,应该是某些颜色的手套全选,最后再+1,否则...应该不是最优的(感性理解...);

    所以处理出这种 L,找 S(T) >= L 中 R(T) 最大的,可以用单调栈实现;

    用结构体存一种集合的 S(T) 和 R(T),S(T) 同时也是上面所说的 L,按 S(T) 从小到大排序,不断弹栈令栈内 R(T) 是单调递减的,就能找到一个结构体后面(S(T) >= L)的 R(T) 最大值,对于一个 R(T) 也能去更新前面同等条件最小的 L;

    还有些疑惑之处...就是一些 L 相同的结构体,排序时按 r<y.r 或 r>y.r 都可以过...但是感觉应该是 r<y.r 才对!后面就可以把前面都弹掉,则栈里后面元素的 S(T) 一定 >= L+1;

    但如果栈里最后只留下一个值...难道因为这样构造所以不会出现这种情况?

    总之勉强A了,思路还是很妙的。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int const xn=25;
    int n,a[xn],b[xn],ansl,ansr,sta[1<<20],top;
    struct N{
      int l,r;
      bool operator < (const N &y) const
      {return l==y.l?r<y.r:l<y.l;}//
    }f[1<<20];
    int rd()
    {
      int ret=0,f=1; char ch=getchar();
      while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
      while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
      return f?ret:-ret;
    }
    void update(int x,int y)
    {
      if(ansl+ansr>x+y||(ansl+ansr==x+y&&ansl>x))
        ansl=x,ansr=y;
    }
    int main()
    {
      n=rd(); int pl=0,pr=0,tot=0;
      for(int i=1;i<=n;i++)a[i]=rd();
      for(int i=1;i<=n;i++)b[i]=rd();
      for(int i=1;i<=n;i++)
        {
          if(a[i]&&!b[i])pl+=a[i];
          else if(!a[i]&&b[i])pr+=b[i];
          else if(a[i]&&b[i])a[++tot]=a[i],b[tot]=b[i];
        }
      int mx=(1<<tot);
      for(int i=0;i<mx;i++)
          for(int j=1;j<=tot;j++)
        {
          if(i&(1<<(j-1)))f[i].l+=a[j];
          else f[i].r+=b[j];
        }
      sort(f,f+mx);
      for(int i=0;i<mx;i++)
        {
          while(top&&f[sta[top]].r<f[i].r)top--;
          sta[++top]=i;
        }
      ansl=1e9; ansr=1e9;
      for(int i=1;i<top;i++)update(f[sta[i]].l+1,f[sta[i+1]].r+1);
      printf("%d
    %d
    ",ansl+pl,ansr+pr);
      return 0;
    }
  • 相关阅读:
    CF 936C Lock Puzzle——构造
    LOJ 2980 「THUSCH 2017」大魔法师——线段树
    LOJ 2979 「THUSCH 2017」换桌——多路增广费用流
    LOJ 2978 「THUSCH 2017」杜老师——bitset+线性基+结论
    LOJ 2997 「THUSCH 2017」巧克力——思路+随机化+斯坦纳树
    LOJ 2557 「CTSC2018」组合数问题 (46分)
    bzoj 3158 千钧一发 —— 最小割
    CF1092 D & E —— 思路+单调栈,树的直径
    bzoj 5120 无限之环 & 洛谷 P4003 —— 费用流(多路增广SPFA)
    bzoj 1070 修车 —— 费用流
  • 原文地址:https://www.cnblogs.com/Zinn/p/9880913.html
Copyright © 2011-2022 走看看