zoukankan      html  css  js  c++  java
  • 状态压缩刷题

    所谓状态压缩,大多数就是用二进制01形式将状态表示出来,运用位运算完成状态的查看和转移;基本上数据范围是n<=15;

    P4906 小奔关闹钟

    这是很裸的状态压缩。我们要关闭所有的开关,但是开关是相连的;

    有一个很好地条件是,开关最多能波及到两层;一个开关的变化,直接关联的会变化,间接变化的也会变,但是间接变化的不会再传递下去;

    所以我们将每个开关关掉后的状态记录下来;

    设二进制1是关闭状态,当状态变为0时更新答案即可;

    要排除自己关自己的情况;

    二进制枚举关哪一个就行了;

    一个开关不需要关两遍及以上,相当于没干什么;

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=1e6+10;
    int res[maxn];
    int a[30][30];
    int n;
    int ans=2147483647;
    void dfs(int x,int now,int cnt)
    {
        if(x==n+1)
        {
            if(now==0)
            {
                ans=min(ans,cnt);
            }
            return ;
        }
        
        dfs(x+1,now,cnt);
        now^=res[x];
        dfs(x+1,now,cnt+1);
        now^=res[x];
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int m;
            scanf("%d",&m);
            for(int j=1;j<=m;j++)
            {
                int x;
                scanf("%d",&x);
                a[i][x]=1;
            }
        }
        for(int i=1;i<=n;i++)
        {
            res[i]^=(1<<i);
            for(int j=1;j<=n;j++)
            {
                if(a[i][j]&&i!=j)
                {
                    res[i]^=(1<<j);
                    for(int k=1;k<=n;k++)
                    {
                        if(a[j][k]&&j!=k)
                        {
                            res[i]^=(1<<k);
                        }
                    }
                }
            }
        }
        //int sum=0;
        int sum=(1<<(n+1))-2;
        /*for(int i=1;i<=n;i++)
        {
            sum+=1<<i;
        }*/
        //printf("%d
    ",sum);
        dfs(1,sum,0);
        
        if(ans==2147483647) printf("Change an alarm clock,please!");
        else printf("%d",ans);
        return 0; 
        
    }
    View Code

    P2622 关灯问题II

    这道题的状态比上面的题多一点点,但是基本类比;不同的是我们这次用DP转移。

    设f[i]为当前为i状态时,最少关灯次数;

    二进制数1表示灯开着,f[0]为答案;

    每次判断每个按键能控制的灯,按完后的状态为now,从按之前的转移过来;

    (1<<(k-1)&now)表示当前第k个灯还亮着;

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2e5+10;
    
    int n,m;
    int a[120][120];
    int f[maxn];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        
        memset(f,0x7f,sizeof(f));
        f[(1<<n)-1]=0;
        for(int i=(1<<n)-1;i>=0;i--)
        {
            
            for(int j=1;j<=m;j++)
            {
                int now=i;
                for(int k=1;k<=n;k++)
                {
                    if(a[j][k]==0) continue;
                    if(a[j][k]==1&&((1<<(k-1)&now))) now^=(1<<(k-1));
                    else if(a[j][k]==-1&&!((1<<(k-1))&now)) now^=1<<(k-1);
                }
                f[now]=min(f[now],f[i]+1);
            }
        }
        if(f[0]==2139062143) printf("-1");
        else printf("%d",f[0]);
        return 0;
    }
    View Code

    P2915 [USACO08NOV]奶牛混合起来Mixed Up Cows

    我们需要的是方案数,DP跑不了;

    怎么设计状态?

    设f[i][j]为第i头牛再队尾时,情况为j时的合法方案数;

    二进制1表示牛在队列里;

    我们枚举哪头牛再队尾的情况;

    再枚举哪头牛再他的前面,状态直接搬运过来;

    初始化 队列中只有一头牛的方案数为1;

    枚举时判断混乱条件;

    最后的答案是加和;

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=1e5+10;
    typedef long long ll;
    ll f[20][maxn];
    int n,x;
    int a[20];
    
    ll ans;
    int main()
    {
        scanf("%d%d",&n,&x);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        
        for(int i=1;i<=n;i++)
        {
            f[i][1<<(i-1)]=1;
        }
        for(int i=1;i<1<<n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(f[j][i]) continue;
                if(!(i&(1<<(j-1)))) continue;
                int now=i^(1<<(j-1));
                for(int k=1;k<=n;k++)
                {
                    if(j==k) continue;
                    if(!(now&(1<<(k-1)))||abs(a[j]-a[k])<=x) continue;
                    f[j][i]+=f[k][now];
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            ans+=f[i][(1<<n)-1];
        }
        printf("%lld",ans);
        return 0;
    }
    View Code

    P2473 [SCOI2008]奖励关

    这个题是状态压缩+期望;

    将每个物品的前提条件状态压缩存起来,当前状态能吃的时候比较一下即可;

    设f[i][j]表示在第1轮到第i1轮内宝物是否取过的状态为j,第i轮到第K轮的最大期望得分。

    但是我们需要逆推,因为正着枚举有些状态是并没有被算出来的;

    如果当前状态能选当前物品

    f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+(dd)p[k]);

    下一轮是要选k的,才能加上p[k];

    每次都要/n,因为选k的概率是1/n;

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef double dd;
    
    dd f[110][1<<15];
    
    int k,n;
    int p[20];
    int res[20];
    
    int main()
    {
        scanf("%d%d",&k,&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&p[i]);
            int x;
            while(scanf("%d",&x)&&x)
            {
                res[i]|=1<<(x-1);
            }
        }
        
        for(int i=k;i>=1;i--)
        {
            for(int j=0;j<(1<<n);j++)
            {
                for(int k=1;k<=n;k++)
                {
                    if((res[k]&j)==res[k])
                    {
                        f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+(dd)p[k]);
                    }
                    else f[i][j]+=f[i+1][j];
                }
                f[i][j]/=(dd)n;
            }
        }
        
        printf("%.6lf",f[1][0]);
        return 0;
    }
    View Code
  • 相关阅读:
    Windows Phone云应用开发实践之(三)——OData
    Windows Phone 云应用开发实践之(二)Windows Live SDK
    使用Windows Azure Web Sites构建托管的WEB应用
    Windows Phone Mango开发实践——GB2312编码转换
    概念 : 用户>角色>权限 的管理(RoleBased Access Control)
    angular uirouter 正则
    过程 : 概念 : 结构 jobbox jobPost
    css 居中,中央
    javascript : detect at the end of bottom
    设计理念 : popup login 在前后台
  • 原文地址:https://www.cnblogs.com/WHFF521/p/11660437.html
Copyright © 2011-2022 走看看