Ural 1519. Formula 1 优美的插头DP
今天早上学了插头DP的思想和最基础的应用,中午就开始敲了,岐哥说第一次写不要看别人代码,利用自己的理解一点点得写出来,这样才锻炼代码能力!于是下午慢慢地构思轮廓,一点点地敲出主体代码,其实是很磨蹭的,由于要考虑好多东西,而且昨晚2点睡的有点困,最后终于磨蹭出来了,第一次的代码搓没关系,自己写的才重要。然后果然不出我所料,调试到了晚上才A了(一个郁闷的错误)。。。A的感觉真的是爽呀,虽然搞了差不多一天。当然自己写了自己想的代码后也要把代码优化,不然队友看不懂自己代码就囧了。。。
插头DP,建议大家想学的好好看看陈丹琦的国家集训队论文,这是个优美的DP。
http://www.docin.com/p-46797997.html
#include <stdio.h> #include <string.h> #define LL __int64 const int mod = 10007; // 哈希表 struct HASH{ int head[mod+10], E, next[80000]; LL val[80000], cnt[80000]; void init() { memset(head, -1, sizeof(head)); E = 0; } int findhash(LL x) { return (x%mod + mod)%mod; } void add(LL x, LL sum) { int u = findhash(x); for(int i = head[u];i != -1;i = next[i]) if(val[i] == x) { cnt[i] += sum; return ; } val[E] = x; cnt[E] = sum; next[E] = head[u]; head[u] = E++; } }biao1, biao2; int c[22], n, m, d[22]; // 编码 void get(LL x) { for(int i = m+1;i >= 1; i--) { c[i] = x&7; x /= 8; } } // 解码 LL getval() { LL ret = 0; for(int i = 1;i <= m+1; i++) { ret |= d[i]; ret *= 8; } ret /= 8; return ret ; } // 转化成最小表示法 void change() { int vis[22]; memset(vis, 0, sizeof(vis)); int num = 1; for(int i = 1;i <= m+1;i ++) { if(!d[i]) continue; if(!vis[d[i]]) { vis[d[i]] = num; d[i] = num++; } else { d[i] = vis[d[i]]; } } } void fuzhi() { for(int i = 1;i <= m+1;i ++) d[i] = c[i]; } char s[22][22]; int main() { int i, j, k, l; while(scanf("%d%d", &n, &m) != -1) { for(i = 1;i <= n; i++) scanf("%s", s[i]+1); int tot = 0; for(i = 1;i <= n; i++) for(j = 1;j <= m; j++) if(s[i][j] == '.') tot++; if(tot%2==1 || tot < 4) { puts("0"); continue; } int tox = -1, toy = -1; for(i = 1;i <= n; i++) for(j = 1;j <= m; j++) if(s[i][j] == '.') { tox = i; toy = j; } biao1.init(); biao1.add(0, 1); LL ans = 0; for(i = 1;i <= n; i++){ for(j = 0;j <= m; j++){ biao2.init(); for(l = 0;l < biao1.E; l++) { get(biao1.val[l]); if(j == m) { for(int ii = 2;ii <= m+1; ii++) d[ii] = c[ii-1]; d[1] = 0; change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); continue; } if(c[j+1] && !c[j+2]) { // 有左插头无上插头 if(s[i][j+1] != '.') continue; if(j+2 <= m) { fuzhi(); d[j+1] = 0;d[j+2] = c[j+1]; change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } if(i < n) { fuzhi(); change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } } else if(!c[j+1] && c[j+2]) { // 有上插头无左插头 if(s[i][j+1] != '.') continue; if(i < n) { fuzhi(); d[j+1] = c[j+2]; d[j+2] = 0; change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } if(j+2 <= m) { fuzhi(); change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } } else if(!c[j+1] && !c[j+2]) { // 左和上都无插头 if(s[i][j+1] != '.') { fuzhi(); change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); continue; } if(j+2 <= m && i < n) { fuzhi(); d[j+1] = d[j+2] = 13; change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } } else { // 左和上都有插头 , 要判断左和上插头是否连通 if(c[j+2] == c[j+1]) { int tot = 0; for(int ii = 1;ii <= m+1; ii++) if(c[ii]) tot++; if(tot == 2 && i == tox && j+1 == toy) ans += biao1.cnt[l]; } else { if(s[i][j+1] != '.') continue; fuzhi(); for(int ii = 1;ii <= m+1; ii++) if(ii != j+1 && ii != j+2 && d[ii] == d[j+1]) { d[ii] = d[j+2]; break; } d[j+1] = d[j+2] = 0; change(); LL now = getval(); biao2.add(now, biao1.cnt[l]); } } } biao1 = biao2; } } printf("%I64d\n", ans); } return 0; }