Online Judge | Problem Set | Authors | Online Contests | User | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest |
想了一个给力的剪枝,附上0MS代码首先我们可以想到情况是对称的,也就是说枚举所有的分类情况中,有一半是冗余的。 举个例子来说,有5个节点,1、2、3分配为A组,4、5分配给B组,这个分类的策略所计算的结果和1、2、3分配为B组,4、5分配给A组是一样的。那么怎么去除这一半的冗余呢,其实很简单,我们强制第1个节点分配给A组,那么后面不管怎么分,全体情况都不会有重复的了。 但是情况还是太多,20个节点有2^19中分类情况,我的剪枝方法是这样的: 给n个节点,它能达到的流量上限是多少?我们知道将n个节点分为两个部分,每个部分不为空,那么对于一个部分来说,内部节点直接的边是完全没用,需要去掉的。当我们把n个节点等分时(也就是A组n/2个节点,B组n/2个节点),去掉的边最少(同学们可以用代数方法表示下,可以发现是个二次函数,n/2是最低点),大约为m = n^2/4 - n/2(n为偶数情况)。既然我们n个节点进行分组至少要去掉这么多边,那么我们将边的总和去掉边权值最少的m条边,就是一个流量的最高上限。 通过上面的方法我们可以很快的在DFS中估计出当前策略向下枚举的最高流量极限,这样就可以剪枝了。根据我的试验,这个剪枝方法非常管用,在n=20的情况下,枚举到树的叶子节点的情况基本没有超过10的,很给力。 附上我的0MS代码: #include <iostream> #include <cstdio> #include <queue> #include <algorithm> #include <vector> using namespace std; int c[20][20]; int n; bool v[20]; int maxTraffic; // remain[i]代表 i~n-1的节点能产生的极限最大traffic int remain[30]; int num=0; int calc(){ int res = 0; for( int i=0; i<n; ++i ){ for( int j=i+1; j<n; ++j ){ if( v[i] ^ v[j] ) //v[i] != v[j] res += c[i][j]; } } return res; } int calcPartial( int idx, int partial ){ int res = partial; for( int i=0; i<idx; ++i ){ if( v[i] ^ v[idx] ) res += c[i][idx]; } return res; } int calcOpt( int idx, int partial ){ int t = partial; int a,b; for( int i=idx + 1; i<n; ++i ){ a=b=0; for( int j=0; j<=idx; ++j ){ if( v[j] ) a += c[i][j]; else b += c[i][j]; } t += max( a, b ); } t += remain[idx+1]; return min( remain[0], t ); } void dfs( int idx, int partial ){ if( idx == n ){ maxTraffic = max( maxTraffic, partial ); ++num; return; } v[idx] = true; int p = calcPartial( idx, partial ); if( calcOpt( idx, p ) > maxTraffic ) dfs( idx+1, p ); v[idx] = false; p = calcPartial( idx, partial ); if( calcOpt( idx, p ) > maxTraffic ) dfs( idx + 1, p ); } void calcRemain(){ int cnt,p,q,sum = 0, asum = 0; priority_queue< int, vector<int>, greater<int> > Q; vector<int> v; v.reserve( 200 ); remain[n] = remain[n-1] = 0; for( int i=n-2; i>-1; --i ){ cnt = n - i; p = cnt >> 1; q = cnt - p; cnt = ( (p * (p-1) ) >> 1 ) + ( (q*(q-1)) >> 1 ); for( int j=i+1; j<n; ++j ){ Q.push( c[i][j] ); sum += c[i][j]; } remain[i] = sum; while(cnt--){ v.push_back( Q.top() ); remain[i] -= Q.top(); Q.pop(); } while( !v.empty() ){ Q.push( v.back() ); v.pop_back(); } } } int main(){ while( ~scanf( "%d", &n ) ){ for( int i=0; i<n; ++i ){ for( int j=0; j<n; ++j ){ scanf( "%d", &c[i][j] ); } } num = 0; maxTraffic = 0; calcRemain(); v[0] = true; dfs( 1, 0 ); printf( "%d\n", maxTraffic ); } return 0; } Followed by:
Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator