題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1964
題意:每個格子之間的牆壁有個花費,求用一個環經過每個格子僅一次的最小花費。求最小值花費。
思路:一個簡單的插頭dp,相當於一個n*m的地圖裏找一個單迴路覆蓋全部點的最小花費,每個點記錄向右走和向下走的花費。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 100000007
const int maxn = 1000009;
const int Hash = 10007;
int n,m;
int bit = 7 , inc = 3;
int cur , pre;
int code[20];
int vis[20];
int g[12][12][2];
struct hash_table
{
int head[Hash] , size;
LL state[maxn];
int next[maxn] , value[maxn];
void clear()
{
size = 0;
Clean(head,-1);
}
void push( LL S , int V )
{
int index = S % Hash;
for( int k = head[index]; k != -1; k = next[k] )
if ( state[k] == S )
{
value[k] = min(V,value[k]);
return;
}
state[size] = S , value[size] = V;
next[size] = head[index] , head[index] = size++;
}
}dp[2];
void decode( LL S , int m )
{
for( int i = 0; i <= m; i++ ) code[i] = S & bit , S >>= inc;
}
inline LL encode( int m )
{
LL ans = 0;
int now = 1;
Clean(vis,-1);
vis[0] = 0;
for( int i = m; i >= 0; i-- )
{
if ( -1 == vis[ code[i] ] ) vis[code[i]] = now++;
code[i] = vis[ code[i] ];
ans <<= inc;
ans |= code[i];
}
return ans;
}
int pos( int x , int y )
{
return ( x - 1 ) * m + y;
}
void init()
{
char str[100];
scanf("%d%d",&n,&m);
getchar();
gets(str);
rep(i,1,n)
{
gets(str);
rep(j,1,m-1) g[i][j][0] = str[2*j] - '0';
if ( i != n )
{
gets(str+1);
rep(j,1,m) g[i][j][1] = str[2*j] - '0';
}
}
gets(str);
}
bool check()
{
for( int i = 0 ; i <= m; i++ ) if ( code[i] ) return false;
return true;
}
void DP( int x , int y , int k )
{
decode( dp[pre].state[k] , m );
int left = code[y-1] , up = code[y];
code[y-1] = code[y] = 0;
int V = dp[pre].value[k];
if ( !left && !up )
{
if ( x < n && y < m )
{
code[y-1] = code[y] = bit;
dp[cur].push( encode(m) , V + g[x][y][0] + g[x][y][1]);
}
}
else if ( !left || !up )
{
if ( x < n ) code[y-1] = up + left , dp[cur].push( encode(m) , V + g[x][y][1] );
code[y] = code[y-1] = 0;
if ( y < m ) code[y] = up + left , dp[cur].push( encode(m) , V + g[x][y][0] );
}
else if ( left != up ) //合併不同的連通分量
{
for( int i = 0; i <= m; i++ )
if ( code[i] == left ) code[i] = up;
dp[cur].push( encode(m) , V );
}
else if ( x == n && y == m ) dp[cur].push( encode(m) , V );//在最後一個點形成迴路
}
void solve()
{
dp[0].clear();
dp[0].push( 0 , 0 );
cur = 0;
for( int i = 1; i <= n; i++ )
{
pre = cur , cur ^= 1 , dp[cur].clear();
for( int k = 0; k < dp[pre].size; k++ ) dp[cur].push( dp[pre].state[k]<<inc , dp[pre].value[k] );
for( int j = 1; j <= m; j++ )
{
pre = cur , cur ^= 1 , dp[cur].clear();
for( int k = 0; k < dp[pre].size; k++ ) DP(i,j,k);
}
}
for( int k = 0; k < dp[cur].size; k++ )
if ( dp[cur].state[k] == 0 )
{
printf("%d\n",dp[cur].value[k]);
return;
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
init();
solve();
}
return 0;
}