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]要慢一些的(主要应该是m小的时候k<tot清空比清空hash表快)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define HASH 100007 #define STATE 500010 #define maxd 15 int maze[maxd][maxd]; int code[maxd]; int n,m; struct HASHMAP{ int head[HASH]; int state[STATE],nxt[STATE]; int f[STATE]; int sz; void clear(){sz=0; memset (head,-1, sizeof (head));} void push( int st, int ans){ int h=st%HASH; for ( int i=head[h];i!=-1;i=nxt[i]){ if (st==state[i]){ f[i] = f[i]<ans?f[i]:ans; return ; } } state[sz]=st,nxt[sz]=head[h],f[sz]=ans; head[h]=sz++; } }hm[2]; void decode( int st){ for ( int i=m;i>=0;--i) code[i]=st&3,st>>=2; } int encode(){ int ret=0; for ( int i=0;i<=m;++i) ret=ret<<2|code[i]; return ret; } void shift(){ for ( int i=m;i;--i) code[i]=code[i-1]; code[0]=0; } int ans; int zo,oz,oo; void dpblank( int i, int j, int cur){ int mv = j==m?2:0; int all = (1<<(2*(m+1)-mv) ) -1; for ( int k=0;k<hm[cur].sz;++k){ int st = hm[cur].state[k]; int left = st&(oo>>(2*(j-1))), up = st&(oo>>(2*j)); int L = left>>(2*(m-j+1)), U = up>>(2*(m-j)); int cnt = ((L>>1)+(U>>1))&1; if (i==1 || U==2 || maze[i-1][j]==U){ int st2 = st^left^up; if (cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j)); hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]); } if (hm[cur].f[k]+2<ans) if (i==1 || U==2 || maze[i-1][j]==U){ int st2 = st^left^up; if (!cnt) st2 = st2 | (zo>>(2*(j-1))) | (zo>>(2*j)); hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+2); } if (hm[cur].f[k]+1<ans) if (i==1 || U==2 || maze[i-1][j]!=U){ int st2 = st^left^up; if (j>1 && L!=2) st2 = st2 ^ (zo>>(2*(j-2))); st2 = st2 | (oz>>(2*(j-1))) | (oz>>(2*j)); hm[cur^1].push((st2>>mv)&all, hm[cur].f[k]+1); } } } void solve(){ zo = 1<<(2*m); oz = 2<<(2*m); oo = 3<<(2*m); int cur=0; hm[0].clear(); hm[0].push(0,0); for ( int i=1;i<=n;++i){ for ( int j=1;j<=m;++j){ hm[cur^1].clear(); dpblank(i,j,cur); cur^=1; } } for ( int k=0;k<hm[cur].sz;++k){ bool yes= true ; decode(hm[cur].state[k]); for ( int j=1;j<=m;++j){ if (code[j]!=2 && code[j]!=maze[n][j]){ yes= false ; break ; } } if (yes) ans = ans<hm[cur].f[k]?ans:hm[cur].f[k]; } } int main(){ int ca=0; while (~ scanf ( "%d%d" ,&n,&m) && n){ printf ( "Case #%d: " ,++ca); ans=0; for ( int i=1;i<=n;++i) for ( int j=1;j<=m;++j) scanf ( "%1d" ,maze[i]+j), ans+=maze[i][j]; ans*=2; solve(); printf ( "%d\n" ,ans); } return 0; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步