学的算法不多,这是自己的第一份题解
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5513
题意比较复杂,给你一个网格图及各边权值, n*m,n<800, m<8, 问要生成一棵包含所有格点的权值最小的生成树,权值最小是多少。
还问了一个,就是对于这个最小生成树,每个格点有一个值,值为1+(有向上连边+1, 有向左连边+1), 要把所有格点的值全部乘起来,另外如果有多棵最小生成树,就把所有权值也都乘起来。。。听上去就比较复杂
用滚动数组,g[0/1][st]表示更新到这个点时现在的生成树的权值,st表示前m个点的联通状态
将网格线从左上往右下扫,st先用最小表示法,枚举出这个点前面m个点的联通状态,然后将这些用8进制压起来(m<8), 对于现在这个点i,实际上和他右边相连的只有i-1和i-m两个点,四种情况进行枚举更新,注意每种情况的更新有要求,比如更新完后第i-m个点一定要放入连通块中,并且连的时候不能成环
还有一个要注意的就是可以把所有的转移状态都预处理出来
原题 NOI2007 生成树计数http://www.lydsy.com/JudgeOnline/problem.php?id=1494
有很多博客有Tree定理,是关于生成树的,很值得学习
一个大神的题解
http://blog.csdn.net/whjpji/article/details/7617796
#include
#include
#include
#include
typedef __int64 ll;
using namespace std;
const int mod = 1e9+7;
int left[805][10], up[805][10], v[1005], a[8], b[8];
ll f[2][1005], g[2][1005];
int n, m, cnt, qe = 0;;
const ll maxn = 4294967295;
int nxt[4][1005];
int find( int sta )
{
int l, r, mid, pos = -1;
l = 1, r = cnt-1;
while( l <= r ){
mid = (l+r)>>1;
if( v[mid] == sta ){
pos = mid;
break;
}
if( v[mid] > sta ) r = mid-1;
else l = mid+1;
}
return pos;
}//二分法找出需要的状态
void dfs( int x, int y, int maxn )
{
int sta, i, j;
if( x > y ){
sta = a[1];
for( i = 2; i <= m; i ++ ){
sta = sta << 3;
sta = sta|a[i];
}
v[cnt++] = sta;
return;
}
for( a[x] = 1; a[x] <= maxn; a[x] ++ ){
if( a[x] == maxn )dfs( x+1, m, maxn+1 );
else dfs( x+1, m, maxn );
}
}//最小表示法处理出所有的点
int modify( int x, int id )
{
int b[10], i, j, k, r[10], ans = 0;
int vis[10];
memset( vis, 0, sizeof(vis) );
k = x;
for( i = m; i > 0 ; i -- ){
b[i] = k & 7;
if( id == 1 && b[i] == 1 ){
b[i] = a[m];
}
k >>= 3;
}
for( i = 1, j = 1; i <= m; i ++ ){
if( vis[b[i]] == 0 ){
r[i] = j;
vis[b[i]] = j;
j ++;
}
else{
r[i] = vis[b[i]];
}
}
ans = r[1];
for( i = 2; i <= m; i ++ ){
ans <<= 3;
ans = ans | r[i];
}
return ans;
}//将新的点加入原状态
void intit()
{
int i, j;
scanf("%d %d", &n, &m);
for( int i = 1; i <= n; i ++ )
for( int j = 2; j <= m; j ++ )
scanf("%d", &left[i][j]);
for( int i = 2; i <= n; i ++ )
for( int j = 1; j <= m; j ++ )
scanf("%d", &up[i][j]);
cnt = 1;
memset( a, 0, sizeof(a) );
dfs( 1, m, 1 );
sort( v+1, v+cnt );
for( i = 1; i < cnt; i ++ ){
int st = v[i], nk, pos;
for( int l = m, tmp = st; l >= 1; l -- ){
a[l] = tmp & 7;
tmp >>= 3;
}
nk = modify( ( (st << 3) | a[m] ) & ( ( 1 << (3*m) ) - 1 ), 1 );
pos = find(nk);
nxt[0][i] = pos;
nk = modify( ( (st << 3) | a[m] ) & ( ( 1 << (3*m) ) - 1 ), -1 );
pos = find(nk);
nxt[1][i] = pos;
nk = modify( ( (st << 3) | 1 ) & ( ( 1 << (3*m) ) - 1 ), -1 );
pos = find(nk);
nxt[2][i] = pos;
nk = modify( ( (st << 3) | 0 ) & ( ( 1 << (3*m) ) - 1 ), -1 );
pos = find(nk);
nxt[3][i] = pos;
}//预处理所有状态转移来加速
}
void solve()
{
int now = 0, bef = 1, i, j, ok, p, q;
memset( g, 0, sizeof(g) );
for( int k = 0; k < 1000; k ++ )f[now][k] = f[bef][k] = maxn;
f[now][1] = 0; g[now][1] = 1;
for( i = 1; i <= n; i ++ )
for( j = 1; j <= m; j ++ ){
if( i == 1 && j == 1 )continue;
swap( now, bef );
memset( g[now], 0, sizeof(g[now]) );
for( int k = 0; k < 1000; k ++ )f[now][k] = maxn;
for( p = 1; p < cnt; p ++ ){
int st = v[p];
if( f[bef][p] == maxn )continue;
ok = 0;
for( int l = 1, tmp = st; l < m; l ++ ){
if( (tmp & 7) == 1 ){
ok = 1;
}
tmp >>= 3;
}
for( int l = m, tmp = st; l >= 1; l -- ){
a[l] = tmp & 7;
tmp >>= 3;
}
if( i > 1 && j > 1 && a[1] != a[m] ){//确保不成环
int tmp = f[bef][p]+left[i][j]+up[i][j];
int cu = nxt[0][p];
if( tmp == f[now][cu] ){
g[now][cu] = ( g[now][cu] + g[bef][p] * 3 ) % mod;
}
else if( tmp < f[now][cu] ){
f[now][cu] = tmp;
g[now][cu] = g[bef][p]*3%mod;
}
}
if( j > 1 && ok ){
int tmp = f[bef][p]+left[i][j];
int cu = nxt[1][p];
if( tmp == f[now][cu] ){
g[now][cu] = ( g[now][cu] + g[bef][p] * 2 ) % mod;
}
else if( tmp < f[now][cu] ){
f[now][cu] = tmp;
g[now][cu] = g[bef][p]*2%mod;
}
}
if( i > 1 ){
int tmp = f[bef][p]+up[i][j];
int cu = nxt[2][p];
if( tmp == f[now][cu] ){
g[now][cu] = ( g[now][cu] + g[bef][p] * 2 ) % mod;
}
else if( tmp < f[now][cu] ){
f[now][cu] = tmp;
g[now][cu] = g[bef][p]*2%mod;
}
}
if( ok ){
int tmp = f[bef][p];
int cu = nxt[3][p];
if( tmp == f[now][cu] ){
g[now][cu] = ( g[now][cu] + g[bef][p] ) % mod;
}
else if( tmp < f[now][cu] ){
f[now][cu] = tmp;
g[now][cu] = g[bef][p]%mod;
}
}//四种更新
}
// for( int k = 1; k < cnt; k ++ ){
// printf("%d %d %d %I64d %I64d\n", i, j, k, f[now][k], g[now][k]);
// }
}
printf("Case #%d: %I64d %I64d\n", ++qe, f[now][1], g[now][1] );
}
int main()
{
int T;
//freopen("out.txt", "w", stdout);
scanf("%d", &T);
while( T -- ){
intit();
solve();
}
}