• CF79D Password


    题目链接

    题意:给定长度为n的0/1序列,初始值都为0。你每次可以在给定的l个长度中的(a_i)并将序列中长度为(a_i)的部分取反。使得最终状态为(x_1)~(x_k),求最少取反次数。

    分析:看到这种区间整体变化的题,我们应该直觉想到差分(也可能想到线段树

    先求出差分后的数组,然后每次操作相当于对距离为(a_i)的两点取反。

    我们可以用类似求最短路的方法求出取反i,j位的最小操作次数。

    因为最后为1的位置 (leq) 10,所以操作的次数 (leq) 20。

    所以就可以大力状压了,方程见代码。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int dp[1<<20], num[1<<20], low[1<<20], f[10005], dis[10005], mn[22][22], len[105], cnt[22];
    int n, k, l, tot=0;
    queue<int> q;
    
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        for (; ch<'0' || ch>'9'; ch=getchar()) if (ch=='-') f=-1;
        for (; ch>='0' && ch<='9'; ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    void Get_Shortest_Path(int rt)
    {
        memset(dis, 0x3f, sizeof(dis)); dis[cnt[rt]]=0;
        q.push(cnt[rt]);
        while (!q.empty())
        {
            int u=q.front(); q.pop();
            for (int i=1; i<=l; i++)
            {
                if (u+len[i]<=n+1 && dis[u+len[i]]>dis[u]+1)
                    {dis[u+len[i]]=dis[u]+1; q.push(u+len[i]);}
                if (u-len[i]>0 && dis[u-len[i]]>dis[u]+1)
                    {dis[u-len[i]]=dis[u]+1; q.push(u-len[i]);}
            }
        }
        for (int i=1; i<=tot; i++) mn[rt][i]=dis[cnt[i]];
    }
    
    int main()
    {
        n=read(); k=read(); l=read();
        for (int i=1; i<=k; i++) {int x=read(); f[x]=1;}
        for (int i=1; i<=n+1; i++) if (f[i]^f[i-1]) cnt[++tot]=i;//差分
        for (int i=1; i<=l; i++) len[i]=read();
        for (int i=1; i<=tot; i++) Get_Shortest_Path(i);//预处理取反i,j位的最小操作次数
        low[1]=0; for (int i=2; i<(1<<tot); i++) low[i]=low[i>>1]+1;//预处理log_2
        for (int i=1; i<(1<<tot); i++) num[i]=num[i-(i&-i)]+1;//预处理二进制位数
        memset(dp, 0x3f, sizeof(dp)); dp[0]=0;
        for (int i=0; i<(1<<tot); i++)
        {
            if (num[i]&1) continue;
            int j=((1<<tot)-1)^i; j=low[j&-j]+1;
            for (int k=j+1; k<=tot; k++)
                dp[i|(1<<(j-1))|(1<<(k-1))]=
                    min(dp[i|(1<<(j-1))|(1<<(k-1))],
                        dp[i]+mn[j][k]);
        }//状压
        printf("%d
    ",dp[(1<<tot)-1]==inf?-1:dp[(1<<tot)-1]);
        return 0;
    }
    
    

    我才不会告诉你们这题有双倍经验的

  • 相关阅读:
    Mvc4.0添加商品到Cookie
    个人作业——词频统计
    利用飞信虚拟框架开发绿色.NET程序
    专用服务器和共享服务器的区别(转)
    我做项目对Excel进程处理问题
    通过SQL Server 2000的链接服务器访问Sybase数据库
    结合地理信息系统开发的项目
    模仿Smartphone的开始菜单效果
    如何修改Windows Mobile手机系统颜色
    个人负责的第一个地图应用项目框架截图
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10147396.html
Copyright © 2020-2023  润新知