• 洛谷P3943 星空——题解


      一道很好的锻炼思维难度的题,如果您能在考场上直接想出来的话,提高组450分以上就没问题了吧。(别像作者一样看了好几篇题解才勉强会)

      先提取出题目大意:给定一个长度n<=40000的01串,其中1的个数<=8,有m种操作,每次操作都是把一个该操作对应长度的区间取反,或者说异或上1,求使整个串变为只有0的串的最小操作次数。 

      首先对于一次操作,肯定不能暴力地一个个去取反吧。优化区间操作,要么用数据结构,要么用前缀和或差分。其实这里可以用差分来优化:建立新的下标最大为n+1、下标正常从1开始的d数组,d[i]=a[i]^a[i-1],每次区间修改时,设修改的区间的左端点为l,右端点为r,只要让a[l]和a[r+1]分别异或上1就行了。

        解释一下:这里的差分中的“差”已经不能简单理解为减法做差了,而是逻辑上的“差距”。差分维护的是相邻元素间的逻辑关系,从而使能从初始状态(a[0])通过差分数组表达的逻辑关系推出某个位置上a的值(从形式上看就是求前缀)。对于异或来说,正好满足这样的性质:我们读入串时从a[1]开始读入,那a[0]没管它的话就会是0,那么发现从它开始向后与d数组做前缀异或时,设当前做到第i个位置了(即当前值=a[0]^d[1]^d[2]^……^d[i]),则当前值就是a[i]的值,同样对于差分优化的区间操作来说,对  左端点  和   右端点+1  处取反后,在求一遍前缀异或,发现对于那个要修改的区间,真的就取反了。(可以这么考虑:对于区间中的位置来说,修改后再求完前缀后,每位都比修改前的这位多异或了1,故取反;对于区间后面的位置,修改后再求完前缀后,每位都比修改前的这位多异或了2个1,就不会改变,总体上看,这个区间就被取反了。这要依赖于异或这个运算可交换且有单位元(么元)、且1有对于异或的逆元(其实所有数都有)(逆元可不只限于取模哟))。因为要能实现右端点等于n的区间修改,所以d数组的最大下标为n+1,同时也是为下文1的个数为偶数的结论做铺垫。

      那么问题就变为:给定一个长度n<=40001的01串,其中1的个数<=16,有m种操作,每次操作都是把下标差该操作对应长度的两个数取反,求使整个串变为只有0的串的最小操作次数。

        解释一下:考虑将原串的1全都拿出来后一个个加入到一个全是0的串u里,形成一个与原串完全一样的串,看看u串对应的差分数组的变化,发现每次加入一个1,差分数组要么新增2个1(加入u串的1在u串中左右没有相邻的1),要么有一个1往后或前移一个位置(加入u串的1在u串中的左边或右边中只有一边有相邻的1),要么减少2个1(加入u串的1在u串中的左边和右边都相邻的1),因为要从u串中加最多8个1,显然对应的差分数组最多就16个1,同时差分数组中1的个数一定为偶数。

        显然对于每次操作取反的两个数中一定有一个1,不然这次操作不但没用,还多出了2个1,浪费次数还增多任务。考虑每次取反的两个数:

          1、有1个1:那么结果是原来1的位置现在变成了0,原来0的位置现在变成了1。形象化地,1从原来的位置移动到了另一个位置

          2、有2个1:那么这两个1都会变为0,形象化地一个1移动到了有1的位置,两个1碰到一起就消失了。

      于是问题又被形象化地转化为:给定一个长度n<=40001的01串,其中1的个数<=16,有m种移动长度,每次移动都是把1个1移动这个移动相应的移动长度,若2个1碰到一起(在同一个位置)就会消失,求使整个串的所有1消失的最小移动次数。

        如果我们知道了这些1两两碰到一起消失的最小移动次数,跑个状压DP就可以喽。而这些1两两碰到一起消失的最小移动次数,正可以对每个1跑一遍BFS求得(把串的每一位看成一个点,向这个点能一次移动到的所有点都连一条长度为1的边),时间复杂度O(kmn),完全可以接受。至于状压DP,推荐写O(k*(2的2*K次方))的DP,如果写O((k的平方)*(2的2*K次方))的DP,虽然能过这个题,但是很容易到考场上被卡。关于O(k*(2的2*K次方)),我们可以从当前状态向以后状态转移,当前状态第一个没有被消去的1迟早要被消去,不如先消去,转移到包括这个1的状态,容易发现每一个消去所有1的状态,都可以通过这样的策略实现出来,所以这样是正确的。

    最后看看代码吧:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<queue>
      5 
      6 using namespace std;
      7 
      8 const int N=40005,M=64,K=8;
      9 
     10 int n,k,m,x,a[N],d[N],caolen[M+2],d1[K<<2],cntd1,lst[N],nxt[N*M<<1],to[N*M<<1],cnt;
     11 int wei[N],vis[N];
     12 
     13 long long dp[1<<K*2],dis[K<<3][K<<3];
     14 
     15 char ch;
     16 
     17 inline int read()
     18 {
     19     x=0;
     20     ch=getchar();
     21     while(!isdigit(ch)) ch=getchar();
     22     while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
     23     return x;
     24 }
     25 
     26 inline void addedge(int u,int v)
     27 {
     28     nxt[++cnt]=lst[u];
     29     lst[u]=cnt;
     30     to[cnt]=v;
     31 }
     32 
     33 struct node{
     34     int w,dlen;
     35 }head;
     36 
     37 queue<node>q,ling;
     38 
     39 inline void bfs(int w,int ord)//变量含义:这个1在原串中的位置,这个1在d1(将d中的1又单独存了一下)中的位置 
     40 {
     41     memset(vis,0,sizeof vis);
     42     q=ling;
     43     vis[w]=1;
     44     int cntin=1,t;
     45     q.push((node){w,0});
     46     while(!q.empty())
     47     {
     48         head=q.front();
     49         q.pop();
     50         for(int e=lst[head.w];e;e=nxt[e])
     51             if(!vis[t=to[e]])
     52             {
     53                 vis[t]=1;
     54                 cntin++;
     55                 if(d[t])
     56                     dis[ord][wei[t]]=head.dlen+1;
     57                 if(cntin==n) 
     58                     return;
     59                 q.push((node){t,head.dlen+1});
     60             }
     61     }
     62 }
     63 
     64 inline void init()
     65 {
     66     n=read(),k=read(),m=read();
     67     for(int i=1;i<=k;++i)
     68         a[read()]=1;
     69     for(int i=1;i<=m;++i)
     70         caolen[i]=read();
     71     for(int i=1;i<=n+1;++i)
     72     {
     73         d[i]=a[i]^a[i-1];
     74         if(d[i])
     75         {
     76             d1[++cntd1]=i;
     77             wei[i]=cntd1;
     78         }
     79             
     80     }
     81     for(int i=1;i<=n+1;++i)
     82         for(int j=1;j<=m;++j)
     83         {
     84             if(i+caolen[j]<=n+1)
     85                 addedge(i,i+caolen[j]);
     86             if(i-caolen[j]>0)
     87                 addedge(i,i-caolen[j]);
     88         }
     89     memset(dis,0x3f,sizeof dis);
     90     for(int i=1;i<=cntd1;i++)
     91         bfs(d1[i],i);
     92 }
     93 
     94 int main()
     95 {
     96     init();    
     97     int lim=(1<<cntd1)-1;
     98     memset(dp,0x3f,sizeof dp);
     99     dp[0]=0;//下标为1的消去状态(下标的二进制第i位为1:d1中第i个1已被消去) 
    100     for(int i=0;i<=lim;++i)
    101     {
    102         int j=0;
    103         while(i&(1<<j))
    104             j++;
    105         for(int t=j+1;t<cntd1;t++)
    106         {
    107             if(!(i&(1<<t)))
    108             dp[i|(1<<j)|(1<<t)]=min(dp[i|(1<<j)|(1<<t)],dp[i]+dis[j+1][t+1]);
    109         }
    110     }
    111     printf("%lld",dp[lim]);
    112     return 0;
    113 }

      

      

  • 相关阅读:
    ArrayList和HashTable妙用一
    面向过程,面向对象的深入理解一
    jquery中限制部分字段不能输入
    MySql用int存储时间
    android绑定sqlite数据库与程序一起发布
    extjs 4和jquery整合
    jQuery.support 的实现方式
    常用JavaScripts方法
    SVN错误信息大全
    android封装的menu自定义菜单列表
  • 原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11765183.html
Copyright © 2020-2023  润新知