HDU 4949 Light(插头dp、位运算)

比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题。。。。

因为写完之后,无限TLE。。。

直到后来用位运算代替了我插头dp常用的decode、encode、shift三个函数以及改改HASH值才勉强过的。。。7703ms

题意:给一个N*M的01矩阵,每次可以选一个格子进行2种操作,①翻转邻居格子②翻转邻居格子和自己。输出最小的总操作数使得矩阵全为0.

显然每个格子有4种操作(一、不操作;二、①②;三、①;四、②)。

一开始写的时候用2位表示一个插头,一位用于表示翻转当前格子,一位表示插头的源头需要被翻转。然后就是2*3*(4^10)感觉有点不科学。

后来发现,其实,我们可以这样归类,①不操作(花费0);②翻自己(花费2);③翻转邻居(花费1);这样就是2*3*(3^10)

其中③包括2种情况,事实上,如果对一个格子A进行了第③种操作,那这个格子的邻居格子BCDE做任何操作,A都可以熄灯。

还有就是,答案一定小于等于一开始矩阵的1的个数的2倍,可以用这个进行一定程度的剪枝。

然后就是各种位运算了。。。。搞得我都晕了。。。

另外,其实,因为这样插头dp需要消耗很多额外的花费(清空hash表什么的),所以速度应该是比直接dp[i][j][k]要慢一些的(吧?)。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 using namespace std;
  5
  6
  7 #define HASH 100007
  8 #define STATE 500010
  9 #define maxd 15
 10
 11 int maze[maxd][maxd];
 12 int code[maxd];
 13 int n,m;
 14 struct HASHMAP{
 15     int head[HASH];
 16     int state[STATE],nxt[STATE];
 17     int f[STATE];
 18     int sz;
 19     void clear(){sz=0;memset(head,-1,sizeof(head));}
 20     void push(int st,int ans){
 21         int h=st%HASH;
 22         for(int i=head[h];i!=-1;i=nxt[i]){
 23             if(st==state[i]){
 24                 f[i] = f[i]<ans?f[i]:ans;
 25                 return ;
 26             }
 27         }
 28         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
 29         head[h]=sz++;
 30     }
 31 }hm[2];
 32 void decode(int st){
 33     for(int i=m;i>=0;--i) code[i]=st&3,st>>=2;
 34 }
 35 int encode(){
 36     int ret=0;
 37     for(int i=0;i<=m;++i) ret=ret<<2|code[i];
 38     return ret;
 39 }
 40 void shift(){
 41     for(int i=m;i;--i) code[i]=code[i-1];
 42     code[0]=0;
 43 }
 44 int ans;
 45 int zo,oz,oo;
 46 void dpblank(int i,int j,int cur){
 47     int mv = j==m?2:0;
 48     int all = (1<<(2*(m+1)-mv) ) -1;
 49     for(int k=0;k<hm[cur].sz;++k){
 50         int st = hm[cur].state[k];
 51         int left = st&(oo>>(2*(j-1))), up = st&(oo>>(2*j));
 52         int L = left>>(2*(m-j+1)), U = up>>(2*(m-j));
 53         int cnt = ((L>>1)+(U>>1))&1;
 54         if(i==1 || U==2 || maze[i-1][j]==U){
 55             int st2 = st^left^up;
 56             if(cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j));
 57             hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]);
 58         }
 59         if(hm[cur].f[k]+2<ans)
 60         if(i==1 || U==2 || maze[i-1][j]==U){
 61             int st2 = st^left^up;
 62             if(!cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j));
 63             hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+2);
 64         }
 65         if(hm[cur].f[k]+1<ans)
 66         if(i==1 || U==2 || maze[i-1][j]!=U){
 67             int st2 = st^left^up;
 68             if(j>1 && L!=2) st2 = st2 ^ (zo>>(2*(j-2)));
 69             st2 = st2 | (oz>>(2*(j-1))) | (oz>>(2*j));
 70             hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+1);
 71         }
 72     }
 73 }
 74 void solve(){
 75     zo = 1<<(2*m);
 76     oz = 2<<(2*m);
 77     oo = 3<<(2*m);
 78     int cur=0;
 79     hm[0].clear();
 80     hm[0].push(0,0);
 81     for(int i=1;i<=n;++i){
 82         for(int j=1;j<=m;++j){
 83             hm[cur^1].clear();
 84             dpblank(i,j,cur);
 85             cur^=1;
 86         }
 87     }
 88     for(int k=0;k<hm[cur].sz;++k){
 89         bool yes=true;
 90         decode(hm[cur].state[k]);
 91         for(int j=1;j<=m;++j){
 92             if(code[j]!=2 && code[j]!=maze[n][j]){
 93                 yes=false;
 94                 break;
 95             }
 96         }
 97         if(yes) ans = ans<hm[cur].f[k]?ans:hm[cur].f[k];
 98     }
 99 }
100 int main(){
101     int ca=0;
102     while(~scanf("%d%d",&n,&m) && n){
103         printf("Case #%d: ",++ca);
104         ans=0;
105         for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%1d",maze[i]+j), ans+=maze[i][j];
106         ans*=2;
107         solve();
108         printf("%d\n",ans);
109     }
110     return 0;
111 }

HDU 4949 Light(插头dp、位运算)

时间: 2024-11-09 00:43:40

HDU 4949 Light(插头dp、位运算)的相关文章

hdu 2721(字符串处理,位运算 暴力)

Persistent Bits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 201    Accepted Submission(s): 116 Problem Description WhatNext Software creates sequence generators that they hope will produce

hdu 3257 Hello World!(位运算 &amp; 模拟)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3257 Hello World! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 476    Accepted Submission(s): 180 Problem Description Your task is to print ...

基于DP+位运算的RMQ算法

来源:http://blog.csdn.net/y990041769/article/details/38405063 RMQ算法,是一个快速求区间最值的离线算法,预处理时间复杂度O(n*log(n)),查询O(1),所以是一个很快速的算法,当然这个问题用线段树同样能够解决. 问题:给出n个数ai,让你快速查询某个区间的的最值. 算法分类:DP+位运算 算法分析:这个算法就是基于DP和位运算符,我们用dp[i ][j]表示从第 i 位开始,到第 i + 2^j -1 位的最大值或者最小值. 那么

HDU 5735 Born Slippy(拆值DP+位运算)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5735 [题目大意] 给出一棵树,树上每个节点都有一个权值w,w不超过216,树的根为1,从一个点往根的方向走,可以得到他的祖先序列,现在需要从v1点的祖先序列中挑选出一定数量的点,组成数列v1,v2,v3……vm,要求vi是vi-1的祖先,求dp[v1]=max(dp[vi]+(w[v1] opt w[vi])),opt是一种运算,在题目中可为xor,or或者and,最后求出ans=sum_{i

HDU 5119 Happy Matt Friends(dp+位运算)

题意:给定n个数,从中分别取出0个,1个,2个...n个,并把他们异或起来,求大于m个总的取法. 思路:dp,背包思想,考虑第i个数,取或者不取,dp[i][j]表示在第i个数时,异或值为j的所有取法.dp[i][j] = dp[i - 1][j] + dp[i - 1][j ^ a[i]]); 其中dp[i - 1][j]表示不取第i个数,dp[i - 1][j & a[i]]表示取第i个数,由于40比较大,所以用滚动数组优化,后一个状态需要前一个来推导,而和前一个之前的所有的没有关系,所以之

hdu 4336 Card Collector (概率dp+位运算 求期望)

题目链接 Card Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2711    Accepted Submission(s): 1277Special Judge Problem Description In your childhood, do you crazy for collecting the beaut

HDU 5014 Number Sequence(位运算)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5014 解题报告:西安网赛的题,当时想到一半,只想到从大的开始匹配,做异或运算得到对应的b[i],但是少了一个操作,ans[i] = temp,却没有想到ans[temp] = i;所以就一直卡在这里了,因为我不确定这样是不是一一对应的,好吧,也没有想到这里,就差这么点了. 1 #include<cstdio> 2 #include<cstring> 3 #include<iost

hdu 5491 The Next (位运算)

http://acm.hdu.edu.cn/showproblem.php?pid=5491 题目大意:给定一个数D,它的二进制数中1的个数为L,求比D大的数的最小值x且x的二进制数中1的个数num满足s1 <= num <= s2 分析:将D+1变成n,求其二进制数中1的个数L   (1)如果L在(s1, s2)范围内,直接输出 (2)如果num<s1,从右到左找0的最小位i,将该位的0改成1(即:1的个数少了,增加一个1),n的值则增加2^i(即:n += 2^i)  (3) 如果n

[poj 1185] 炮兵阵地 状压dp 位运算

Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队 (山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击