题意 n*m(n,m <= 7 )的图里边最多18个串 每个串长度不超过k (k <= 5),每次选两个消除,类似连连看的方式,但是只能变一次方向,消除两个获得的价值是两个字符串位置一样的个数,假如 一共有 i 个,得到价值就是 s[i] (0 <= i <= k). 问消除所有的获得最大价值。
思路
状压dp,预处理一些东西,加速dp
算了复杂度感觉是 , 但实际可能有剪枝,达不到这个复杂度,莽一发过了。
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 5; char s[10][10][10]; int cnt; struct node { int x, y; }p[25]; int can[25][25][2], dp[N], val[25][25], a[15]; void solve(int j, int k) { int x1 = p[j].x, x2 = p[k].x; if(x1 > x2) swap(x1, x2); int y1 = p[j].y, y2 = p[k].y; if(y1 > y2) swap(y1, y2); int v1 = 0, v2 = 0; for(int i = 0; i < cnt; i++) { if(p[i].x == p[j].x && p[i].y >= y1 && p[i].y <= y2) v1 |= (1 << i); if(p[i].y == p[k].y && p[i].x >= x1 && p[i].x <= x2) v1 |= (1 << i); if(p[i].x == p[k].x && p[i].y >= y1 && p[i].y <= y2) v2 |= (1 << i); if(p[i].y == p[j].y && p[i].x >= x1 && p[i].x <= x2) v2 |= (1 << i); } v1 ^= (1 << j) | (1 << k); v2 ^= (1 << k) | (1 << j); can[j][k][0] = v1; can[j][k][1] = v2; } int T, n, m, k, all; int get(int i, int j) { int num = 0; for(int h = 0; h < k; h++) { if(s[p[i].x][p[i].y][h] == s[p[j].x][p[j].y][h]) num++; } return num; } void init() { all = (1 << cnt) - 1; for(int i = 0; i < cnt; i++) for(int j = i + 1; j < cnt; j++) { solve(i, j); val[i][j] = a[get(i, j)]; } } int main() { //#ifndef ONLINE_JUDGE // freopen("h.in", "r", stdin); //#endif scanf("%d", &T); while(T--) { cnt = 0; scanf("%d %d %d", &n, &m, &k); for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { scanf(" %s", s[i][j]); if(s[i][j][0] != '-') p[cnt++] = {i, j}; } } for(int i = 0; i <= k; i++) scanf("%d", &a[i]); for(int sta = 0; sta < (1 << cnt); sta++) dp[sta] = -1e9; dp[0] = 0; init(); for(int sta = 0; sta < (1 << cnt); sta++) { for(int i = 0; i < cnt; i++) { if(sta & (1 << i)) continue; for(int j = i + 1; j < cnt; j++) { if(sta & (1 << j)) continue; if((can[i][j][0] & sta) == can[i][j][0] || (can[i][j][1] & sta) == can[i][j][1]) dp[sta | (1 << i) | (1 << j)] = max(dp[sta | (1 << i) | (1 << j)], dp[sta] + val[i][j]); } } } printf("%d\n", dp[all]); } return 0; }