题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1519
陈丹琦的《基于连通性状态压缩的动态规划问题》的论文上的题目
题意:
给你m*n的棋盘,有的格子是障碍,问共有多少条回路使得经过每个非障碍格子恰好一次。
做法:
论文上的思路讲的很清楚了,这里用最小表示法来做。
因为(2 ≤ N, M ≤ 12),所以最多出现6个联通块,所以用8进制来做。
1 #include <bits/stdc++.h> 2 using namespace std; 3 int N, M; 4 #define maxn 15 5 #define HASH 30007 6 #define STATE 200010 7 int mp[maxn][maxn]; 8 int pos[maxn]; 9 int mark, ex, ey; 10 int code[maxn]; 11 struct HashMap 12 { 13 int head[HASH], next[STATE], cnt; 14 long long state[STATE]; 15 long long sum[STATE]; 16 void init() 17 { 18 cnt = 0; 19 memset(head, -1, sizeof(head)); 20 } 21 void push(long long sta, long long val) 22 { 23 int h = sta%HASH; 24 for(int i = head[h]; i != -1; i = next[i]) 25 { 26 if(state[i] == sta) //如果这种状态已经存在 27 { 28 sum[i] += val; 29 return; 30 } 31 } 32 state[cnt] = sta; 33 sum[cnt] = val; 34 next[cnt] = head[h]; 35 head[h] = cnt++; 36 } 37 }hashmp[2];//滚动数组 38 void decode(int *code, long long sta) 39 { 40 for(int i = M; i >= 0; i--) 41 { 42 code[i] = sta&7; 43 sta >>= 3; 44 } 45 } 46 long long encode(int *code) //转化成2进制 47 { 48 int cnt = 1; 49 int vis[maxn]; 50 memset(vis, -1, sizeof(vis)); 51 vis[0] = 0; //0不连通 52 long long sta = 0; 53 54 for(int i = 0; i <= M; i++) 55 { 56 if(vis[code[i]] == -1) vis[code[i]] = cnt++; 57 code[i] = vis[code[i]]; 58 sta <<= 3; 59 sta |= code[i]; 60 } 61 return sta; 62 } 63 void AtLast(int *code) 64 { 65 for(int i = M; i > 0; i--) code[i] = code[i-1]; 66 code[0] = 0; 67 } 68 void slove() 69 { 70 mark = 0; 71 hashmp[mark].init(); 72 hashmp[mark].push(0, 1); //开始时轮廓线在最上方 73 74 for(int i = 1; i <= N; i++) 75 { 76 for(int j = 1; j <= M; j++) 77 { 78 hashmp[mark^1].init();//初始化 79 if(mp[i][j] == 0) //不能放置 80 { 81 for(int k = 0; k < hashmp[mark].cnt; k++) 82 { 83 decode(code, hashmp[mark].state[k]); 84 code[j-1] = code[j] = 0; 85 if(j == M) AtLast(code); 86 hashmp[mark^1].push(encode(code),hashmp[mark].sum[k]); 87 } 88 } 89 else //可以放置 90 { 91 for(int k = 0; k < hashmp[mark].cnt; k++) 92 { 93 decode(code, hashmp[mark].state[k]); 94 int left, up; 95 left = code[j-1]; up = code[j]; 96 if(left == 0 && up == 0) //情况1:没有上插头和左插头,新建一个联通分量 97 { 98 if(mp[i+1][j] && mp[i][j+1]) 99 { 100 code[j-1] = code[j] = 13; //如果mp[i][j+1] = 1,则不可能出现j == M的情况 101 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]); 102 } 103 } 104 else if(left && up) //情况2:有上插头和左插头 105 { 106 if(left != up) //上插头和左插头不联通,合并联通分量。 107 { 108 code[j-1] = code[j] = 0; //则不可能再有右插头和下插头 109 for(int ii = 0; ii <= M; ii++) 110 { 111 if(code[ii] == up) code[ii] = left; 112 } 113 if(j == M) AtLast(code); 114 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]); 115 } 116 else if(left == up) //上插头和左插头联通 117 { 118 119 if(i == ex && j == ey) //这种情况只可能出现在最后一个可以摆放的位置 120 { 121 code[j-1] = code[j] = 0; 122 if(j == M) AtLast(code); 123 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]); 124 } 125 } 126 } 127 else if((left&&(!up)) || (up&&(!left)))//情况3:上插头和左插头只存在一个 128 { 129 int val; 130 if(left) val = left; 131 else val = up; 132 if(mp[i][j+1]) //右边的格子可走 133 { 134 code[j] = val; code[j-1] = 0; 135 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]); 136 } 137 if(mp[i+1][j]) //下面的格子可走 138 { 139 code[j-1] = val; code[j] = 0; 140 if(j == M) AtLast(code); 141 hashmp[mark^1].push(encode(code), hashmp[mark].sum[k]); 142 } 143 } 144 } 145 } 146 mark^=1; 147 } 148 } 149 } 150 int main() 151 { 152 // freopen("in.txt", "r", stdin); 153 // freopen("out.txt", "w", stdout); 154 while(~scanf("%d%d", &N, &M)) 155 { 156 memset(mp, 0, sizeof(mp)); 157 bool flag = false; 158 for(int i = 1; i <= N; i++) 159 { 160 char str[15]; 161 scanf("%s", str); 162 for(int j = 0; j < M; j++) 163 { 164 if(str[j] == ‘.‘) 165 { 166 mp[i][j+1] = 1; 167 ex = i; ey = j+1; 168 flag = true; 169 } 170 else if(str[j] == ‘*‘) mp[i][j+1] = 0; 171 } 172 } 173 if(flag == false) //没有空块 174 { 175 printf("0\n"); continue; 176 } 177 slove(); 178 long long ans = 0;; 179 for(int i = 0; i < hashmp[mark].cnt; i++) 180 { 181 ans += hashmp[mark].sum[i]; 182 } 183 printf("%lld\n", ans); 184 } 185 return 0; 186 }
时间: 2024-10-19 22:31:04