您的位置:首页 > 其它

NOIP 2017 Day2 题2:宝藏 状态压缩

2017-12-19 17:12 344 查看
NOIP 2017 Day2 题2:宝藏 状态压缩

两篇文章写的很好。

摘自: http://blog.csdn.net/qq_38678604/article/details/78572860  写的真好!!
http://blog.csdn.net/PhantomAgony/article/details/78702573


解题思路

首先,很容易发现打通后的道路一定是一棵树,并且,若以起点为根并令其深度为0,则题目中的 K 即为这条路所连向的点的深度。

观察数据范围,n≤12,显然是状压dp:

dp状态:dp[i][S] 表示考虑到树的第i层,前i层已选的点的集合为S(二进制状压)的最小代价。
dp方程(刷表法): 

已知dp[i][S]时,可枚举所有由不在S中的点构成的集合作为第i+1层,则状态转移为

dp[i][S]→dp[i+1][S|S′]+(i+1)×Σ min{G[a][b]|a∈S,b∈S′,S∩S′=∅}
简单一点,就是  dp[i][S]→dp[i+1][S|S′]+(i+1)×sval[S′][S]}

其中sval[A][B]表示集合A到集合B的最短距离,即集合A中所有点到集合B的最短距离之和。可以先预处理出每个点到每个集合的最短距离pval[i][S](也就是点i到集合S中所有点的距离的最小值),然后用pval[i][B]更新sval[A][B]。

dp顺序:由dp方程可得:从小到大枚举层数,再枚举集合即可

边界条件:枚举根节点,设为root,则dp[0][1<<(root−1)]=0


状压相关技巧

若S是U的子集,则S关于U的补集:S∧U
判断点k是否在集合S中(即S的第k−1位是否为1):
S
& (1 << (k-1)) != 0 ? "Yes" : "No";

枚举S的子集:
for(int
i = S; i; i = (i - 1) & S){...}

通过了17组数据,还有三个数据未通过。 待检查

/*
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
*/

#include <bits/stdc++.h>
//#define debug
using namespace std;
const int MAX_N =12;
const int INF=100000;
int G[MAX_N][MAX_N];
int N,M;

int dp[12][1<<12];
int pval[12][1<<12];
int sval[1<<12][1<<12];
int U;
int ans = INT_MAX;

void init(int root){

for(int i = 0; i < N; i++)
for(int s = 0; s <= U; s++)
dp[i][s] = INF;

dp[0][1<<root] = 0;

}

void pre(){

for(int i=0; i<N; i++){

for(int s=0; s<=U; s++){

for(int k=0; k<N; k++){

if(  (s>>k) & 1  ) {
// cout << pval[i][s]  <<endl;
//cout << G[i][k]<<endl;
pval[i][s] =  min(  pval[i][s] , G[i][k] );

}
}

#ifdef debug
cout << i << "  "<< bitset<10>(s) << " "<< pval[i][s] <<endl;
#endif
}

}

for(int s1=0;s1<=U;s1++){
int C= (s1 ^ U);
for(int s2=C; s2>0; s2=(s2-1)&C){

int temp=0;
for(int i=0;i<N;i++){
if( s2>>i & 1) { //若果在s2中
temp += pval[i][s1];
}
}
sval[s2][s1] = ((temp>=INF)?INF:temp);

#ifdef debug
cout << bitset<10>(s2) << "  "<< bitset<10>(s1) << " "<< sval[s2][s1] <<endl;
#endif
}
}

}

int  donggui(){
int  ans=1e9;
for(int root=0;root<N;root++){
init(root);
for(int i=0;i<N;i++){
//i是层次
for(int s1=0; s1<=U ;s1++){

if( dp[i][s1]!=INF){

int C=s1^U;

for(int s2=C;s2>0;s2=(s2-1)&C){

dp[i+1][ s1|s2 ] = min(  dp[i+1][s1|s2],  dp[i][s1] + (i+1)*sval[s2][s1]);//不是sval[s1][s2]
}

}
}
}

#ifdef debug
cout << "root:" << root << endl;
for(int i=0;i<=N;i++){
for(int s=0;s<=U; s++){
cout << i << " " << bitset<10>(s) << " ";
cout << dp[i][s] << " " <<endl;
}
cout <<endl ;
}
#endif

for(int i=0;i<N;i++) ans=min( ans ,dp[i][U]);
}

return(ans);

}

int main(){

cin >>N >> M;
U = (1<<N)-1;

for(int i = 0; i <N; i++)             //initialize g[i][j]
for(int j = 0; j <N; j++)
if(i == j)
G[i][j] = 0;
else  G[i][j] = INF;

for(int i = 0; i <N; i++)
for(int s = 0; s <= U; s++)
pval[i][s] = INF;

/* for(int i = 0; i <N; i++){
for(int s = 0; s <= U; s++)
cout << pval[i][s] <<"  ";
cout << endl;
}*/

for(int s1 = 0; s1 <= U; s1++)
for(int s2 = 0; s2 <= U; s2++)
sval[s1][s2] = INF;

while(M--){
int a,b,v; cin>>a>>b>>v;
G[a-1][b-1] = min( G[a-1][b-1] , v );
G[b-1][a-1] = min( G[b-1][a-1] , v );
}
#ifdef debug
for(int i = 0; i <N; i++)    {         //initialize g[i][j]
for(int j = 0; j <N; j++)
cout<<G[i][j]<<" " ;
cout << endl;
}
#endif

pre();

cout << donggui();

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: