zoukankan      html  css  js  c++  java
  • zoj3777(状态压缩)

    题目阐述:

    给定n个座位,n个人,每个人可以做n个位置中的任意一个,P[i][j]代表第i个人做第j个位置获得的分数,求有多少种排列方式使得获得的分数大于等于M。

    这道题跟数位dp的思想很像,都是穷举可能的方式,不过数位DP由于前缀的影响可以记忆化,这道题由于n较小,可以直接状态压缩.

    定义状态d[i][s][t]代表已经放了i个座位,放的人数集合为s,获得分数为t的排列的数量。

    然后每次暴力枚举每个位置可能会放的人

    d[i][s | (1 << j)][t+ p[j][i]] +=d[i-1] [s] [t];

    重点是如何实现:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #define maxn 13
    using namespace std;
    int d[1<<(maxn)][505]; //表示到达状态s时产生的最大能量
    int n,m;
    int P[maxn][maxn];
    int ans;
    void init()
    {
        ans=0;
        memset(d,0,sizeof(d));
    }
    //GCD
    //求最大公约数
    //O(logn)
    int gcd(int a, int b)
    {
        if (b == 0)
        return a;
        else
        return gcd(b, a%b);
    }
    int isok(int i)
    {
        int t=0;
        while(i)
        {
            if(i&1) t++;
            i>>=1;
        }
        return t;
    }
    void solve()
    {
        int tot=(1<<n)-1;
         d[0][0]=1; //其实可以理解成d[-1][0][0],否则下面就需要特殊处理i等于0
        for(int i=0;i<n;i++)   //代表第i个位置
         {
             for(int s=0;s<=tot;s++)  //遍历所有状态
                {
                        if(isok(s)!=i)   continue ;//检测哪些是前i-1个位置的状态
                        for(int t=0;t<=m;t++)  //遍历所有获得的分数
                        {
                           if(!d[s][t])  continue; //检测哪些是前i-1个位置获得的分数
                          for(int j=0;j<n;j++)  //枚举第i个位置可能放的人
                         {
                             if( s & (1<<j) ) //检测前i-1个位置是否放过
                                continue;
                             int state= s | (1<<j);
                             int MM=min(m,t+P[j][i]);
                             d[state][MM]+=d[s][t];
                         }
                        }
                }
         }
         ans=d[tot][m];
    }
    
    int main()
    {
       // freopen("test.txt","r",stdin);
        int fac[maxn];
        fac[0]=1;
        for(int i=1;i<maxn;i++)
            fac[i]=fac[i-1]*i;
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            init();
            for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
               scanf("%d",&P[i][j]);
            }
            solve();
            int d = gcd(fac[n], ans);
            if (ans == 0)
                printf("No solution
    ");
            else
                printf("%d/%d
    ", fac[n]/d, ans/d);
        }
        return 0;
    }

    bfs实现:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <queue>
    #define maxn 13
    using namespace std;
    int d[1<<(maxn)][505]; //表示到达状态s时产生的最大能量
    int n,m;
    int P[maxn][maxn];
    int ans;
    int gcd(int a, int b)
    {
        if (b == 0)
        return a;
        else
        return gcd(b, a%b);
    }
    
    int bit(int i)
    {
        int t=0;
        while(i)
        {
            if(i&1) t++;
            i>>=1;
        }
        return t;
    }
    struct node
    {
        int s,t,cnt=0;
    };
    int  visit[1<<maxn][505];
    void   bfs()
    {
         queue  < node > que;
         memset(visit,0,sizeof(visit));
         int tot=(1<<n)-1;
         node start,last;
         start.s=0; start.t=0;
         start.cnt=0;
         que.push(start);
         visit[start.s][start.t]=1;
         d[0][0]=1;
         while(!que.empty())
         {
             node cur=que.front();
             que.pop();
             for(int i=0;i<n;i++)
             {
                 if(cur.s & (1<<i))
                    continue;
                 node next  ;
                 next.s= cur.s | (1<<i);
                 next.cnt=cur.cnt+1;
                 next.t= min(m,cur.t+P[i][cur.cnt]); //将第i个人放在当前位置,然后才会加1
                 d[next.s][next.t] += d[cur.s][cur.t];
                 if(visit[next.s][next.t])  //保证只进队一次
                    continue;
                 que.push(next);
                 visit[next.s][next.t]=1;
             }
         }
         ans=d[tot][m];
    }
    
    int main()
    {
      // freopen("test.txt","r",stdin);
        int fac[maxn];
        fac[0]=1;
        for(int i=1;i<maxn;i++)
            fac[i]=fac[i-1]*i;
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
             ans=0;
             memset(d,0,sizeof(d));;
            for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            {
               scanf("%d",&P[i][j]);
            }
            bfs();
            int d = gcd(fac[n], ans);
            if (ans == 0)
                printf("No solution
    ");
            else
                printf("%d/%d
    ", fac[n]/d, ans/d);
        }
        return 0;
    }
  • 相关阅读:
    前端React 条件渲染
    hbuilder小白干货之快捷键大全
    前端React 元素渲染
    mybatis学习笔记五(映射)
    mybatis学习笔记四(配置文件)
    mybatis学习笔记二(sqlsession与开发dao)
    mybatis学习笔记三(动态sql)
    mybatis学习笔记一(mybatis概述)
    必备idea 插件plugins 提高编码效率
    shell提升篇
  • 原文地址:https://www.cnblogs.com/xianbin7/p/4780565.html
Copyright © 2011-2022 走看看