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\n");
        else
            printf("%d/%d\n", 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\n");
        else
            printf("%d/%d\n", fac[n]/d, ans/d);
    }
    return 0;
}
时间: 2024-10-22 13:14:46

zoj3777(状态压缩)的相关文章

胜利大逃亡(续)(状态压缩bfs)

胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7357    Accepted Submission(s): 2552 Problem Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带

uva 818(dfs+图+状态压缩)

题意:有n个环,编号从1到n,给出了一些环环相扣的情况,比如给a和b表示a和b两个环的扣在一起的,每个环都是可以打开的,问最少打开多少个环,然后再扣好,可以让所有的环成为一条链. 题解:状态压缩把所有的打开环的情况枚举出来,然后拿去判断是否成立,更新打开环后的图g[i][j],和每个点的度数,不成立有三种情况,1.计算没有打开的环的度数,如果大于2说明不会有链,2.把没有打开环拿去dfs,访问过就vis[i]++,如果vis[i]>=2说明存在环,3.如果打开的环数num + 1小于链的数量,说

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

uva 11195 Another queen (用状态压缩解决N后问题)

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2136 Problem A Another n-Queen Problem I guess the n-queen problem is known by every person who has studied backtracking. In this problem you s

dp状态压缩

dp状态压缩 动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的就是那种状态很多,不容易用一般的方法表示的动态规划问题,这个就更加的难于把握了.难点在于以下几个方面:状态怎么压缩?压缩后怎么表示?怎么转移?是否具有最优子结构?是否满足后效性?涉及到一些位运算的操作,虽然比较抽象,但本质还是动态规划.找准动态规划几个方面的问题,深刻理解动态规划的原理,开动脑筋思考问题.这才是掌握动态规划的关键. 动态规划最关键的要处理的问题就是位运算的操作,容易出错,状态的设计也直

HDU3001(KB2-J 状态压缩dp)

Travelling Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8103    Accepted Submission(s): 2642 Problem Description After coding so many days,Mr Acmer wants to have a good rest.So travelling is

2017盛大游戏杯 零件组装(状态压缩DP之巧妙枚举子集)

题目链接:2017盛大游戏杯 零件组装 题意: 有n个零件,给你相邻关系和排斥关系,每两块零件组装起来有一个代价,问最少的代价总和是多少. 题解: 考虑状态压缩,dp[i]表示i这个集合为一个零件块. 那么要枚举一下i的子集.O(3^n). 先要预处理一下每个集合的排斥个数和相邻个数,然后容斥一下就可以了. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int

HDU1565(状态压缩dp)

方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8170    Accepted Submission(s): 3095 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数

POJ 1753 Flip game状态压缩+广搜

题意有4*4的16个方格,每个方格有黑白两种颜色,每次点击方格后,被点击方格本身及其上下左右的方格都会改变颜色.给出一组状态,求将这组状态变为全白或者全黑至少需要点击几次.若无法达到,则输出Impossible. 样例输入bwwbbbwbbwwbbwww 样例输出4 思路每个方格只有黑白两种颜色,且只有16个方格,因此可以把每个状态看作一个16位的二进制数(状态压缩),2^16=25536,因此可以用int来保存状态.然后就是BFS暴搜,直到状态为0或者65535(全1)为止. 注意点65535