POJ 1038(状态压缩DP)

18 篇文章 0 订阅
Bugs Integrated, Inc.
Time Limit: 15000MS Memory Limit: 30000K
Total Submissions: 10080 Accepted: 3884
Case Time Limit: 5000MS

Description

Bugs Integrated, Inc. is a major manufacturer of advanced memory chips. They are launching production of a new six terabyte Q-RAM chip. Each chip consists of six unit squares arranged in a form of a 2*3 rectangle. The way Q-RAM chips are made is such that one takes a rectangular plate of silicon divided into N*M unit squares. Then all squares are tested carefully and the bad ones are marked with a black marker. 

Finally, the plate of silicon is cut into memory chips. Each chip consists of 2*3 (or 3*2) unit squares. Of course, no chip can contain any bad (marked) squares. It might not be possible to cut the plate so that every good unit square is a part of some memory chip. The corporation wants to waste as little good squares as possible. Therefore they would like to know how to cut the plate to make the maximum number of chips possible. 
Task 
You are given the dimensions of several silicon plates and a list of all bad unit squares for each plate. Your task is to write a program that computes for each plate the maximum number of chips that can be cut out of the plate.

Input

The first line of the input file consists of a single integer D (1 <= D <= 5), denoting the number of silicon plates. D blocks follow, each describing one silicon plate. The first line of each block contains three integers N (1 <= N <= 150), M (1 <= M <= 10), K (0 <= K <= MN) separated by single spaces. N is the length of the plate, M is its height and K is the number of bad squares in the plate. The following K lines contain a list of bad squares. Each line consists of two integers x and y (1 <= x <= N, 1 <= y <= M) ?coordinates of one bad square (the upper left square has coordinates [1, 1], the bottom right is [N,M]).

Output

For each plate in the input file output a single line containing the maximum number of memory chips that can be cut out of the plate.

Sample Input

2
6 6 5
1 4
4 6
2 2
3 6
6 4
6 5 4
3 3
6 1
6 2
6 4

Sample Output

3
4

Source

CEOI 2002


题意:

一个N*M的网格,需要往里面放置无重叠的2*3小矩阵,其中有k个黑色格子无法放置,问你最多放置多少个。

一道经典DP,黑书上面的例题,虽然没有看过,但是自己看了一些博客还是看懂了,特此写下来解题思路,以便总结。

首先还是看状态,状态的寻找感觉最不好找。首先看关系,因为是一个2*3的矩阵,所以某一行的状态最多和上两行的状态有关,并且只和上两行的状态有关,所以考虑一个状态表示上下两行,所以推出来了3个状态:


0:第N行和第N+1行都为白格

1:第N行为白格,第N+1行为黑格

2:第N行为黑格,第N+1行可以为白格或者黑格


然后考虑如何整合某行的M列状态,开M维数组很麻烦,所以用了状态压缩DP,并且是3进制来表示,因为状态只有3个。我们就可以用某一行M列的各种情况就可以用3进制表示,然后转化为10进制,因为M最大为10,所以最大10进制就是3^10-1,  状态的表达就简单许多了。状态的表示现在就明确了,dp【i】【j】代表第i,i-1行摆放为j方式(把j转化为3进制表示)的时候最大放置结果,

每当每一行的状态确定时,就可以推出第i+1,i行的状态,至于如何求,就是枚举每种可以推出来的状态(也就是DFS函数的作用)

然后就用递推的方式,直到第N行,结果就出来了。


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int n,m,k,x,y,ans,o;
int three[15],q[15],p[15],map[160][15],f[2][60000];

void pre(){
   three[0] = 1;
   for (int i = 1;i <= 11;i ++) three[i] = three[i - 1] * 3;
}

int update(int* p,int* q,int i,int j){
   int cnt = 0,res = 0;
   memset(q,0,sizeof q);
   memset(p,0,sizeof p);
   for (int k = 1;k <= m;k ++){
       p[k] = j % 3;
       j /= 3;
   }
   for (int k = 0;k < m;k ++){
       if (map[i][k + 1]) q[k + 1] = 2,res += three[k] * 2;
       else if (p[k + 1] == 2) q[k + 1] = 1,res += three[k];
       else if (p[k + 1] < 2) q[k + 1] = 0;
   }
   return res;
}

void dfs(int now,int y,int r){
   if (now > ans) ans = now;
   if (y + 1 <= m && p[y] == 0 && p[y + 1] == 0 && q[y] == 0 && q[y + 1] == 0){
       q[y] = 2;
       q[y + 1] = 2;
       int i = r + three[y - 1] * 2 + three[y] * 2;
       if (now + 1 > f[o][i]) f[o][i] = now + 1;
       dfs(now + 1,y + 2,i);
       q[y] = 0;
       q[y + 1] = 0;
   }
   if (y + 2 <= m && q[y] == 0 && q[y + 1] == 0 && q[y + 2] == 0){
       q[y] = q[y + 1] = q[y + 2] = 2;
       int i = r + three[y - 1] * 2 + three[y] * 2 + three[y + 1] * 2;
       if (now + 1 > f[o][i]) f[o][i] = now + 1;
       dfs(now + 1,y + 3,i);
       q[y] = q[y + 1] = q[y + 2] = 0;
   }
   if (now > f[o][r]) f[o][r] = now;
   if (y + 1 <= m) dfs(now,y + 1,r);
}

int main(){
   
   int T;
   cin >> T;
   pre();
   while (T --){
       scanf("%d%d%d",&n,&m,&k);
       memset(map,0,sizeof map);
       for (int i = 1;i <= k;i ++){
           scanf("%d%d",&x,&y);
           map[x][y] = 1;
       }
       
       x = 0;
       for (int i = 1;i <= m;i ++){
           if (map[1][i]) x += 2 * three[i - 1];else x += three[i - 1];
       }
       
       o = 0;
       memset(f,-1,sizeof f);
       f[o][x] = 0;
       ans = 0;
       for (int i = 2;i <= n;i ++){
           o = 1 - o;
           memset(f[o],-1,sizeof f[o]);
           for (int j = 0;j < three[m];j ++) if (f[1 - o][j] != -1){
               int r = update(p,q,i,j);
               dfs(f[1 - o][j],1,r);
           }
       }
       cout << ans << endl;
   }
   
   return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值