• LOJ 2997 「THUSCH 2017」巧克力——思路+随机化+斯坦纳树


    题目:https://loj.ac/problem/2977

    想到斯坦纳树。但以为只能做 “包含一些点” 而不是 “包含一些颜色” 。而且不太会处理中位数。

    其实 “包含一些颜色” 用斯坦纳树做也和普通的一模一样……只是赋初值的时候,遇到该颜色的点就可以更新一下罢了……

    中位数可以二分。每个点除了 “块数” 这个关键字之外,再带一个关键字表示 “a[ ][ ]是否大于二分值” ,用 -1 表示不大于,1表示大于,然后普通地跑一个斯坦纳树,看看第二关键字那一维是否 <= 0 即可。

    至于处理 “从 m 个颜色中选 k 个颜色 ”,可以用随机化的算法!就是每次给每种颜色一个 0~k-1 的映射,然后直接求 “ 0~k-1 都出现 ” 的答案即可。同一种颜色会映射到同一种颜色,所以映射后的 k 种不同颜色,在映射前也是 k 种不同颜色。

    相当于把颜色分成 k 组,要求每组选至少一种颜色。要随机多分几遍组才行。据说 100 次之后正确率就很高了。

    一开始求了两点间距离,然后做斯坦纳树的时候,一个点把所有点都用那个距离更新一遍。非常慢。

    不用求两点间距离,做斯坦纳树,更新的时候只更新四相邻的格子即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    int Mn(int a,int b){return a<b?a:b;}
    const int N=235,INF=1e6+5,K=(1<<5)+5;
    int n,m,k,tot,bh[N][N],dy[N][2],c[N],a[N],tp[N],R;
    int bin[10],tp2[N],cb[N],ans,prn; bool vis[N];
    int xx[4]={-1,0,0,1},yy[4]={0,-1,1,0};
    struct Node{
      int x,y;
      Node(int x=0,int y=0):x(x),y(y) {}
      void init(){x=y=N;}
      bool operator< (const Node &b)const
      {return x==b.x?y<b.y:x<b.x;}
      Node operator+ (const Node &b)const
      {return Node(x+b.x,y+b.y);}
      Node operator- (const Node &b)const
      {return Node(x-b.x,y-b.y);}
    }vl[N],dp[N][K];
    priority_queue<pair<Node,int> >q;
    queue<int> q2;
    Node Mn(Node u,Node v){return u<v?u:v;}
    Node Inv(Node u){return Node(-u.x,-u.y);}
    void init()
    {
      ans=N; prn=INF;
      bin[0]=1;for(int i=1;i<=k;i++)bin[i]=bin[i-1]<<1;
      for(int i=1;i<=tot;i++)tp[i]=a[i];
      sort(tp+1,tp+tot+1); R=unique(tp+1,tp+tot+1)-tp-1;
    }
    Node chk(int mid)
    {
      for(int i=1;i<=tot;i++)vl[i]=Node(1,a[i]<=mid?-1:1);
      for(int i=1;i<=tot;i++)
        {
          for(int j=0;j<bin[k];j++)dp[i][j].init();
          if(c[i]!=-1)dp[i][bin[cb[i]]]=vl[i];//if
        }
      for(int s=1;s<bin[k];s++)
        {
          for(int i=1;i<=tot;i++)
        for(int t=(s-1)&s;t;t=(t-1)&s)
          dp[i][s]=Mn(dp[i][s],dp[i][t]+dp[i][s^t]-vl[i]);
          for(int i=1;i<=tot;i++)q2.push(i),vis[i]=1;
          while(q2.size())
        {
          int k=q2.front(); q2.pop(); vis[k]=0;
          int x=dy[k][0], y=dy[k][1];
          for(int i=0,tx,ty;i<4;i++)
            {
              tx=x+xx[i]; ty=y+yy[i];
              if(!tx||tx>n||!ty||ty>m)continue;
              int v=bh[tx][ty]; if(c[v]==-1)continue;
              if(dp[k][s]+vl[v]<dp[v][s])
            {
              dp[v][s]=dp[k][s]+vl[v];
              if(!vis[v])q2.push(v),vis[v]=1;
            }
            }
        }
        }
    
      int U=bin[k]-1; Node ret=dp[1][U];
      for(int i=2;i<=tot;i++)ret=Mn(ret,dp[i][U]);
      return ret;
    }
    void solve()
    {
      for(int i=1;i<=tot;i++) tp2[i]=rand()%k;
      for(int i=1;i<=tot;i++) cb[i]=tp2[c[i]];
      int l=1,r=R,ret=N,r2=N;
      while(l<=r)
        {
          int mid=l+r>>1;
          Node d=chk(tp[mid]); ret=d.x;
          if(ret==N||ret>ans)break;//
          if(d.y<=0)r2=tp[mid],r=mid-1;
          else l=mid+1;
        }
      if(ret<ans)ans=ret,prn=r2;
      else if(ret==ans)prn=Mn(prn,r2);
    }
    int main()
    {
      int T=rdn(); srand(time(0));
      while(T--)
        {
          n=rdn();m=rdn();k=rdn(); tot=0;
          for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          bh[i][j]=++tot,dy[tot][0]=i,dy[tot][1]=j;
          for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)c[bh[i][j]]=rdn();
          for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)a[bh[i][j]]=rdn();
          init();
          for(int t=1;t<=100;t++)solve();
          if(ans==N)puts("-1 -1");
          else printf("%d %d
    ",ans,prn);
        }
      return 0;
    }
  • 相关阅读:
    火星救援
    Android学习笔记(8)————详细谈谈intent的startActivityForResult()方法
    Android小技巧(二):为ContentProvider添加数据库事务支持
    Android小技巧(一):实现捕获应用的运行时异常
    理解Activity的生命周期
    Android异步处理四:AsyncTask的实现原理
    Android异步处理三:Handler+Looper+MessageQueue深入详解
    Android异步处理二:使用AsyncTask异步更新UI界面
    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面
    Android APK反编译详解(附图)
  • 原文地址:https://www.cnblogs.com/Narh/p/10837278.html
Copyright © 2020-2023  润新知