题目大意:
给定无向图,让前k个点都能到达后k个点(保护地)中的一个,而且前k个点每个需要占据后k个中的一个,相互不冲突
找到实现这个条件达到的选择边的最小总权值
这里很容易看出,最后选到的边不保证整个图是联通的
我们只要计算出每一个连通的最小情况,最后跑一遍dfs就能计算出答案了
那么用dp[i][j]表示 i 点为根得到联通状态为 j 的情况需要选到的边的最小总权值
这个用斯坦纳树的思想就可以做到的
对于每一个状态,都用spfa跑一遍得到最优解
dp[i][j] = min(dp[i][j] , dp[k][j]+w[i][k])
而对于每一个点来说就有 dp[i][j] = min(dp[i][j] , dp[i][k]+dp[i][j-k]) (k&j = k)
最后选出所有符合的状态的联通块,dfs找到最优解
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <queue> 5 #include <iostream> 6 using namespace std; 7 #define pii pair<int,int> 8 const int MAX = 1<<11; 9 const int INF = 0x3f3f3f3f; 10 int T , n , m , k; 11 vector<pii> vec[55]; 12 int dp[55][MAX] , id[55]; 13 bool inq[55]; 14 queue<int> que; 15 16 void init() 17 { 18 for(int i=1 ; i<=n ; i++) vec[i].clear(); 19 memset(dp , 0x3f , sizeof(dp)); 20 } 21 22 void add_edge(int u , int v , int w) 23 { 24 vec[u].push_back(make_pair(v , w)); 25 vec[v].push_back(make_pair(u , w)); 26 } 27 28 void spfa(int cur) 29 { 30 while(!que.empty()){ 31 int u = que.front(); 32 // cout<<"inq: "<<u<<endl; 33 que.pop(); inq[u]=false; 34 int l = vec[u].size(); 35 for(int i=0 ; i<l ; i++){ 36 int to = vec[u][i].first; 37 if(dp[to][cur] > dp[u][cur]+vec[u][i].second){ 38 dp[to][cur] = dp[u][cur]+vec[u][i].second; 39 if(!inq[to]){ 40 inq[to] = true; 41 que.push(to); 42 } 43 } 44 } 45 } 46 } 47 48 void get_id() 49 { 50 memset(id , 0 , sizeof(id)); 51 for(int i=1 ; i<=k ; i++){ 52 id[i] = i , dp[i][1<<(id[i]-1)] = 0; 53 que.push(i); 54 spfa(1<<(id[i]-1)); 55 } 56 for(int i=k+1 , j=n; i<=2*k ; i++ , j--){ 57 id[j] = i , dp[j][1<<(id[j]-1)] = 0; 58 que.push(j); 59 spfa(1<<(id[j]-1)); 60 } 61 for(int i=k+1 ; i<=n-k ; i++) dp[i][0] = 0; 62 } 63 64 void read_edge() 65 { 66 for(int i=0 ; i<m ; i++) { 67 int u , v , w; 68 scanf("%d%d%d" , &u , &v , &w); 69 add_edge(u , v , w); 70 } 71 } 72 73 void solve() 74 { 75 int all = 1<<(2*k); 76 for(int cur=0 ; cur<all ; cur++){ 77 for(int i=1 ; i<=n ; i++){ 78 for(int p=cur&(cur-1) ; p ; p=(p-1)&cur){ 79 if(dp[i][cur] > dp[i][p]+dp[i][cur-p]){ 80 dp[i][cur] = dp[i][p]+dp[i][cur-p]; 81 if(!inq[i]){ 82 que.push(i); 83 inq[i]=true; 84 } 85 } 86 } 87 } 88 spfa(cur); 89 } 90 } 91 92 //求出得到的子树的最小代价 93 int tree[MAX] , ok[MAX] , tot , ret; //ok[]记录那些合法的状态,也就是左半部分的1等于右半部分的1 94 95 bool is_ok(int x) 96 { 97 int cnt = 0; 98 for(int i=0 ; i<k ; i++) if(x&(1<<i)) cnt++; 99 for(int i=k ; i<2*k ; i++) if(x&(1<<i)) cnt--; 100 return cnt == 0; 101 } 102 103 void get_tree() 104 { 105 int all = 1<<(2*k); 106 tot = 0; 107 for(int cur=0 ; cur<all ; cur++){ 108 tree[cur] = INF; 109 for(int i=1 ; i<=n ; i++) tree[cur] = min(tree[cur] , dp[i][cur]); 110 if(cur>0 && is_ok(cur)){ 111 ok[++tot] = cur; 112 // cout<<tot<<" "<<cur<<" "<<tree[cur]<<endl; 113 } 114 } 115 } 116 117 void dfs(int p , int cur , int cost , int all) 118 { 119 if(cost>ret) return; 120 if(cur == all){ 121 ret = min(ret , cost); 122 return; 123 } 124 if(p>tot) return; 125 if(!(cur&ok[p])) dfs(p+1 , cur|ok[p] , cost+tree[ok[p]] , all); 126 dfs(p+1 , cur , cost , all); 127 } 128 129 int main() 130 { 131 // freopen("a.in" , "r" , stdin); 132 scanf("%d" , &T); 133 while(T--) 134 { 135 scanf("%d%d%d" , &n , &m , &k); 136 init(); 137 read_edge(); 138 get_id(); 139 solve(); 140 get_tree(); 141 ret = INF; 142 dfs(1 , 0 , 0 , (1<<(2*k))-1); 143 if(ret<INF) printf("%d\n" , ret); 144 else puts("No solution"); 145 } 146 return 0; 147 }
时间: 2024-10-10 17:34:32