您的位置:首页 > 其它

HDU 5513 Efficient Tree 生成树计数+状态压缩

2016-02-06 23:29 351 查看
学的算法不多,这是自己的第一份题解
题目链接<a target=_blank href="http://acm.hdu.edu.cn/showproblem.php?pid=5513">http://acm.hdu.edu.cn/showproblem.php?pid=5513</a>
题意比较复杂,给你一个网格图及各边权值, 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 生成树计数<a target=_blank href="http://www.lydsy.com/JudgeOnline/problem.php?id=1494">http://www.lydsy.com/JudgeOnline/problem.php?id=1494</a>
有很多博客有Tree定理,是关于生成树的,很值得学习
一个大神的题解
<a target=_blank href="http://blog.csdn.net/whjpji/article/details/7617796">http://blog.csdn.net/whjpji/article/details/7617796</a>
#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>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();}}
4000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息