西电oj 1038 状压dp

西电oj 1038  状压dp

1038: 裁玻璃

时间限制: 1 Sec  内存限制: 128 MB
提交: 33  解决: 4
[提交][状态][讨论版]

题目描述

张老板的玻璃店开张了,生意火爆。今天,隔壁玻璃店的刘老板拿来一块玻璃,意在刁难张老板。刘老板说:“我这块玻璃是由N(行)*M(列)小正方形玻璃拼成的,但是其中有一些玻璃坏了,我希望你现在把它裁成尽量多的2*2的小玻璃,而且这些小玻璃都不能有坏的地方。如果你裁出来的块数不是最多的,我就把你赶出建材市场。”
现在,张老板来拜托你帮他解决这个问题,聪明的你可以告诉张老板,最多能裁出多少块2*2的小玻璃吗?

输入

有多组输入数据,第一行为一个数字T,代表有T组输入数据 (0<T<=30)。
接下来为T组数据,每组数据的第一行为两个正整数N和M(1<=N<=10,1<=M<=10),表示玻璃的大小,接下来的N行、每行M个数字(0或1)表示玻璃中每个位置的状态,0表示这个位置是坏的,1表示这个位置是好的。

输出

一共T行。
对于每组数据,输出一个整数,表示最多的2*2小玻璃块数。

样例输入

2
2 3
1 1 1
1 1 1
4 4
0 0 1 1
0 1 1 1
1 1 1 1
1 1 1 1

样例输出

1
3思路:和poj1185一样,状压dp,dp(i,j)=max(dp(i,j),dp(i-1,k)+sum[j])(状态j,k存在合法且不冲突)    dp表示第i行的状态为j时的玻璃数,按行进行递推,sum[j]表示状态为j时这一行的玻璃数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int maxn=1100;
const int INF=(1<<29);

int N,M;
int Map[maxn];
int dp[maxn][maxn];
int state[maxn],sum[maxn],cnt;

void InitState()
{
    memset(state,0,sizeof(state));
    cnt=0;
    for(int i=0;i<(1<<(M-1));i++){
        if((i&(i<<1))) continue;
        state[cnt++]=i;
    }
}

void InitSum()
{
    memset(sum,0,sizeof(sum));
    for(int i=0;i<cnt;i++){
        for(int j=0;j<M-1;j++){
            if(state[i]&(1<<j)) sum[i]++;
        }
    }
}

bool jud_map(int i,int row)
{
    int s=state[i];
    bool flag=1;
    for(int j=0;j<M-1;j++){
        if(s&(1<<j)){
            if(Map[row]&(1<<j)&&(Map[row]&(1<<(j+1)))&&(Map[row+1]&(1<<j))&&(Map[row+1]&(1<<(j+1)))) continue;
            else{
                flag=0;break;
            }
        }
    }
    return flag;
}

bool jud(int a,int b)
{
    int s=state[a],t=state[b];
    if((s&t)||(s&(t<<1))||((s<<1)&t)) return 0;
    return 1;
}

int main()
{
    int T;cin>>T;
    while(T--){
        cin>>N>>M;
        memset(Map,0,sizeof(Map));
        for(int i=1;i<=N;i++){
            int t;
            for(int j=0;j<M;j++){
                scanf("%d",&t);
                if(t) Map[i]|=(1<<j);
            }
        }
        InitState();
        InitSum();
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=N-1;i++){
            for(int j=0;j<cnt;j++){
                if(!jud_map(j,i)) continue;
                if(i==1){
                    dp[i][j]=max(dp[i][j],sum[j]);
                    continue;
                }
                else{
                    for(int k=0;k<cnt;k++){
                        if((!jud_map(k,i-1))||(!jud(j,k))) continue;
                        dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[j]);
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<cnt;i++){
            if(dp[N-1][i]>ans) ans=dp[N-1][i];
        }
        cout<<ans<<endl;
    }
    return 0;
}

				
时间: 2024-10-13 11:57:57

西电oj 1038 状压dp的相关文章

POJ 1038 状压DP

一个公司生产一种2*3规模的芯片,但是原材料上面有一些地方是不能用来当作芯片材料的,给出原料大小,及上面不能做原料的点,问你怎么分解,可以使生成芯片最大化. 对M进行三进制状压 last数组存储第i-1行和i-2行状态,cur数组存储i行和i-1行状态 cur[k]=2; // 本行k位置和上行k位置都不可用 cur[k]=1; // 本行k位置可用,上行k位置不可用 cur[k]=0; // 本行和上行位置k均可用 必须用滚动数组,否则爆内存 #include "stdio.h" #

light oj 1037 状压dp

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <queue> 5 #include <cstdio> 6 #include <algorithm> 7 #include <map> 8 //#include <time.h> 9 //#include <ext/pb_ds/assoc_container

light oj 1057 状压dp TSP

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <queue> 5 #include <cstdio> 6 #include <algorithm> 7 #include <map> 8 //#include <time.h> 9 //#include <ext/pb_ds/assoc_container

POJ 1038 Bugs Integrated Inc (复杂的状压DP)

\(POJ~1038~~*Bugs~Integrated~Inc:\) (复杂的状压DP) \(solution:\) 很纠结的一道题目,写了大半天,就想练练手,结果这手生的.其实根据之前那道炮兵阵地就不应该写的,但是总觉得自己的思路会好一些,码量又小. 博主的核心思路其实就是用一个二进制数来压缩三行的状态,因为二进制的左移右移很方便.然后就是如果三进制会很不好转移. 我们可以用一个二进制数来预处理压缩出第 \(i\) 往下三行的障碍状态,前 \(m\) 个二进制位表第 \(i\) 行,中间 \

(九度OJ)题目1338:角斗士(状压DP)

题目描述: 角斗士是古罗马奴隶社会的一种特殊身份的奴隶,他们的职责是在角斗场上进行殊死搏斗,为了人们提供野蛮的娱乐.他们的结局或是战死,或者由于表现突出赢得胜利而获得释放. 现在在角斗场里有N个待战的角斗士(1 <=N<=18),每天都将举行一场比赛,为了保持比赛的刺激性,每场比赛前才会在所有当前活着的角斗士之中随机选择两名进行上场拼杀.每场比赛的结束条件即为其中一名被杀死.当进行了N场比赛之后,最后存活的角斗士将被释放.而你将被赋予一个任务,计算出每名角斗士最终存活的概率.我们将提供角斗士之

hdu3681(状压dp)

题意:给你一个n*m的矩阵,'F'是起点.机器人从F出发,走到G可以充电(也可以选择不充,同一个G只能充一次电),走到Y关掉开关,D不能走进,每走一步路(上下左右一格)需要消耗1点电量要求把所有开关关掉,且机器人电量上限最少,并求出该最小电量上限.   充电器数量+开关数量<=15,n,m<=15; 解法:首先我们把充电器和开关当做节点,用bfs求出每个节点之间的距离,然后二分电量,状压dp求解判别式 一开始因为忘记了两个节点之间会存在无法到达的情况,WA了几发,qaq 1 #include&

ZOJ3305Get Sauce 状压DP,

状压DP的题目留个纪念,首先题意一开始读错了,搞了好久,然后弄好了,觉得DFS可以,最后超时,修改了很久还是超时,没办法看了一下n的范围,然后觉得状压可以,但是没有直接推出来,就记忆化搜索了一下,可是一直错,莫名奇妙,然后没办法看了一下题解,发现了下面这个比较好的方法,然后按照这个方程去推,然后敲,也是WA了好多把,写的太搓了,没人家的清楚明了,唉~也算是给自己留个纪念,状压一直做的都不太好~唉~还好理解了, 参考了  http://blog.csdn.net/nash142857/articl

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

(状压dp)uva 10817 Headmaster&#39;s Headache

题目地址 1 #include <bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int MAX=1e5+5; 5 const int INF=1e9; 6 int s,m,n; 7 int cost[125]; 8 //char sta[MAX]; 9 string sta; 10 int able[125]; 11 int dp[125][1<<8][1<<8]; 12 in