zoukankan      html  css  js  c++  java
  • BZOJ3508 开灯 & [校内NOIP2018模拟20181027] 密码锁

    Time Limit: 10 Sec Memory Limit: 128 MB

    Description

    xx作为信息学界的大神,拥有众多的粉丝。为了感谢众粉丝的爱戴,xx决定举办一场晚会。为了气派,xx租了一个巨大的灯屏,这个灯屏有(m)行,每行有(n)个小灯泡。对于每一行灯,有L种操作方法,第i种表示你能将任意长度恰为(A_i)的连续一段灯泡的状态取反(灭变亮,亮变灭)。现对于每一行给定(K)个点,要求这K个点发光,其余点必须保持熄灭状态。求每一行达到目标状态的最小操作数。

    Input

    第一行一个数(m),表示LED屏的行数。

    对于LED屏的每一行:

    第一行为(n,k,L),意义见上。

    第二行为(k)个数,表示要求发光的(k)个点。

    第三行为(L)个数,表示(L)种操作方式。

    Output

    对于LED屏的每一行:如果无法达到目标状态,输出(-1),否则输出最少次数。

    Sample Input

    2
    10 8 2
    1 2 3 5 6 7 8 9
    3 5
    3 2 1
    1 2
    3
    

    Sample Output

    2
    -1
    

    HINT

    对于(100\%)的数据,(Tleq 10)(Nleq 10000)(Kleq 10),(Lleq 100),(1leq A_ileq N)

    Source

    By zjwst960422

    Solution

    一个很神仙的思路。

    发现(N)非常大,但是(K)非常小,显然是状压DP,但是只状压(K)又不太好办。

    于是我们发现,原来序列里只会有(2K)个点是一段(0)与一段(1)的间隔的点(我们这里取前一段的最后一个点)。然后我们又发现,不断地对一个段序列取反,实际上是让这一段和等长的只有(1)的序列异或。而这样之后,取反的区间内,相邻两个点的相对状态不会改变,即相邻两个点是否相等是不会改变的。

    因此,我们对原序列(a_i)做一个这样的处理,维护这个点与后一个点的异或查分:

    [b[i]=a[i] xor a[i+1]quad 0leq i leq n ]

    这样的话,我们对(a)里面连续的一段((l..r))取反,只会改变(b)里面的(b[l-1])(b[r])两个点。

    同时,(a)数组与(b)数组之间的又是唯一确定的关系。所以我们要(A)的末状态,等价于对应的(B)

    然后我们发现,如果把全(0)作为初状态,发光后的作为末状态,这样末状态太乱了,不方便转移。倒不如,倒过来,发光后的为初,全(0)为末。然后(A)(0),对应的(B)也是全(0)的。

    我们发现,我们实际上只是需要把初始的(B)里面的所有(1)全部去掉即可。然后,如果两个点坐标差恰好为一个操作时,就可以操作一次,那就是把这两点取反,中间的点不变!。那么我们要算出只取反(i)(j) 需要的操作次数(f[i][j]),其实只需要从(i)出发跑BFS最短路即可。

    然后考虑如何求出总的操作次数。

    我们发现,(B)数列中最多有(2K) 个点为(1),所以我们只应该把那(2K)个点取反,其他点都不能动。那么我们状压一下这些点。然后就是一个非常显而易见的DP。(dp[S])表示(S)里面的点已经完成了取反的任务。

    [dp[S]=min{dp[C_S{i,j}]+f[i][j] | i,jin S}qquad dp[0]=0 ]

    然后我们类似于愤怒的小鸟的优化,这里会产生很多重复的转移,我们的(i)只需要取(S)中的最小的点就可以了。

    最后答案为(dp[full\_set])

    时间复杂度(O(nmk+2^kk))

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<cstdlib>
    #define inf 1000000000
    #define N 10005
    #define M 2000005
    #define T 45
    using namespace std;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x*=10;x+=ch-'0';ch=getchar();}
        return x*f;
    }
    int n,K,m,cnt;
    int x[N],size[N],a[N],num[N];
    bool vis[N],mark[M];
    int dis[N],d[25][25];
    int q[N];
    int f[M];
    void bfs(int x)
    {
         memset(vis,0,sizeof(vis));
         int t=0,w=1;
         dis[x]=0;q[t]=x;vis[x]=1;
         while(t!=w)
         {
             int now=q[t];t++;
             for(int i=1;i<=m;i++)
             {
                 if(now+size[i]<=n&&(!vis[now+size[i]]))
                 {
                     vis[now+size[i]]=1;
                     dis[now+size[i]]=dis[now]+1;
                     q[w++]=now+size[i];
                 }
                 if(now-size[i]>0&&(!vis[now-size[i]]))
                 {
                     vis[now-size[i]]=1;
                     dis[now-size[i]]=dis[now]+1;
                     q[w++]=now-size[i];
                 }
             }
        }
        for(int i=1;i<=n;i++)
            if(num[i])
            {
                if(!vis[i])d[num[x]][num[i]]=inf;
                else d[num[x]][num[i]]=dis[i];
            }
    }
    int dp(int x)
    {
         if(!x)return 0;
         if(mark[x])return f[x];
         mark[x]=1;
         f[x]=inf;
         int st=0;
         for(int i=1;i<=cnt;i++)
         {
             if(x&(1<<(i-1)))
             {
                 if(!st)st=i;
                 else
                 {
                     if(d[st][i]!=inf)
                     f[x]=min(f[x],dp(x^(1<<(st-1))^(1<<(i-1)))+d[st][i]);
                 }
             }
         }
         return f[x];
    }
    int main()
    {
    	freopen("password.in","r",stdin);
    	freopen("password.out","w",stdout);
        n=read();K=read();m=read();
        for(int i=1;i<=K;i++)
        {
            x[i]=read();
            a[x[i]]=1;
        }
        for(int i=1;i<=m;i++)size[i]=read();
        for(int i=n+1;i;i--)a[i]^=a[i-1];
        n++;
        for(int i=1;i<=n;i++)
            if(a[i])
                num[i]=++cnt;
        for(int i=1;i<=n;i++)
            if(a[i])bfs(i);
        dp((1<<cnt)-1);
        if(f[(1<<cnt)-1]==inf)printf("-1");
        else printf("%d",f[(1<<cnt)-1]);
        return 0;
    }
    
  • 相关阅读:
    Tomcat使用经验
    JAVA异常及其异常处理方式
    微信小程序开发笔记(七)--template模板
    微信小程序开发笔记(六)--实现tab选项卡
    微信小程序开发笔记(五)--swiper实现tab选项卡
    微信小程序开发笔记(四)--轮播图
    微信小程序开发笔记(三)--底部导航栏,顶部标题栏设置
    微信小程序开发笔记(二)
    微信小程序开发笔记(一)
    校门外的树
  • 原文地址:https://www.cnblogs.com/hankeke/p/BZOJ3508.html
Copyright © 2011-2022 走看看