题目大意:

给定无向图,让前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找到最优解

 #include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
#define pii pair<int,int>
const int MAX = <<;
const int INF = 0x3f3f3f3f;
int T , n , m , k;
vector<pii> vec[];
int dp[][MAX] , id[];
bool inq[];
queue<int> que; void init()
{
for(int i= ; i<=n ; i++) vec[i].clear();
memset(dp , 0x3f , sizeof(dp));
} void add_edge(int u , int v , int w)
{
vec[u].push_back(make_pair(v , w));
vec[v].push_back(make_pair(u , w));
} void spfa(int cur)
{
while(!que.empty()){
int u = que.front();
// cout<<"inq: "<<u<<endl;
que.pop(); inq[u]=false;
int l = vec[u].size();
for(int i= ; i<l ; i++){
int to = vec[u][i].first;
if(dp[to][cur] > dp[u][cur]+vec[u][i].second){
dp[to][cur] = dp[u][cur]+vec[u][i].second;
if(!inq[to]){
inq[to] = true;
que.push(to);
}
}
}
}
} void get_id()
{
memset(id , , sizeof(id));
for(int i= ; i<=k ; i++){
id[i] = i , dp[i][<<(id[i]-)] = ;
que.push(i);
spfa(<<(id[i]-));
}
for(int i=k+ , j=n; i<=*k ; i++ , j--){
id[j] = i , dp[j][<<(id[j]-)] = ;
que.push(j);
spfa(<<(id[j]-));
}
for(int i=k+ ; i<=n-k ; i++) dp[i][] = ;
} void read_edge()
{
for(int i= ; i<m ; i++) {
int u , v , w;
scanf("%d%d%d" , &u , &v , &w);
add_edge(u , v , w);
}
} void solve()
{
int all = <<(*k);
for(int cur= ; cur<all ; cur++){
for(int i= ; i<=n ; i++){
for(int p=cur&(cur-) ; p ; p=(p-)&cur){
if(dp[i][cur] > dp[i][p]+dp[i][cur-p]){
dp[i][cur] = dp[i][p]+dp[i][cur-p];
if(!inq[i]){
que.push(i);
inq[i]=true;
}
}
}
}
spfa(cur);
}
} //求出得到的子树的最小代价
int tree[MAX] , ok[MAX] , tot , ret; //ok[]记录那些合法的状态,也就是左半部分的1等于右半部分的1 bool is_ok(int x)
{
int cnt = ;
for(int i= ; i<k ; i++) if(x&(<<i)) cnt++;
for(int i=k ; i<*k ; i++) if(x&(<<i)) cnt--;
return cnt == ;
} void get_tree()
{
int all = <<(*k);
tot = ;
for(int cur= ; cur<all ; cur++){
tree[cur] = INF;
for(int i= ; i<=n ; i++) tree[cur] = min(tree[cur] , dp[i][cur]);
if(cur> && is_ok(cur)){
ok[++tot] = cur;
// cout<<tot<<" "<<cur<<" "<<tree[cur]<<endl;
}
}
} void dfs(int p , int cur , int cost , int all)
{
if(cost>ret) return;
if(cur == all){
ret = min(ret , cost);
return;
}
if(p>tot) return;
if(!(cur&ok[p])) dfs(p+ , cur|ok[p] , cost+tree[ok[p]] , all);
dfs(p+ , cur , cost , all);
} int main()
{
// freopen("a.in" , "r" , stdin);
scanf("%d" , &T);
while(T--)
{
scanf("%d%d%d" , &n , &m , &k);
init();
read_edge();
get_id();
solve();
get_tree();
ret = INF;
dfs( , , , (<<(*k))-);
if(ret<INF) printf("%d\n" , ret);
else puts("No solution");
}
return ;
}

HDU 4085 斯坦纳树的更多相关文章

  1. HDU 4085 斯坦纳树+DP

    https://cn.vjudge.net/problem/HDU-4085 给你n,m,k ,分别表示有n个点,m条边,每条边有一个权值,表示修复这条边需要的代价 从前k个点中任取一个使其和后k个点 ...

  2. hdu 3311 斯坦纳树

    思路:虚拟一个0号节点,将每个点建一条到0号节点的边,权值为挖井需要的价值.并要保证0号节点同另外n个寺庙一样被选择即可. 然后就是求斯坦纳树了. #include<map> #inclu ...

  3. HDU 4085 Peach Blossom Spring 斯坦纳树 状态压缩DP+SPFA

    状态压缩dp+spfa解斯坦纳树 枚举子树的形态 dp[i][j] = min(dp[i][j], dp[i][k]+dp[i][l]) 当中k和l是对j的一个划分 依照边进行松弛 dp[i][j]  ...

  4. HDU 3311 Dig The Wells(斯坦纳树)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=3311 [题意] 给定k座庙,n个其他点,m条边,点权代表挖井费用,边权代表连边费用,问使得k座庙里 ...

  5. HDU - 3311: Dig The Wells (斯坦纳树)

    题意:给你n个寺庙,m个村庄,p条路,现在你要在这n+m个位置中选出若干个位置打井,每个位置打井的费用会告诉你,同时p条路也有修建费用,现在每个寺庙都住着一个和尚,问你最小的费用让这n个和尚都能喝上水 ...

  6. 【BZOJ2595】游览计划(状压DP,斯坦纳树)

    题意:见题面(我发现自己真是越来越懒了) 有N*M的矩阵,每个格子有一个值a[i,j] 现要求将其中的K个点(称为关键点)用格子连接起来,取(i,j)的费用就是a[i,j] 求K点全部连通的最小花费以 ...

  7. hdu4085 Peach Blossom Spring 斯坦纳树,状态dp

    (1)集合中元素表示(1<<i), i从0开始 (2)注意dp[i][ss] = min(dp[i][ss], dp[i][rr | s[i]] + dp[i][(ss ^ rr) | s ...

  8. bzoj 4006 [JLOI2015]管道连接(斯坦纳树+状压DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4006 [题意] 给定n点m边的图,连接边(u,v)需要花费w,问满足使k个点中同颜色的 ...

  9. bzoj 2595 [Wc2008]游览计划(斯坦纳树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2595 [题意] 给定N*M的长方形,选最少权值和的格子使得要求的K个点连通. [科普] ...

随机推荐

  1. iOS开发 UIPanGestureRecognizer手势抽象类

    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@sel ...

  2. Hbase之插入数据

    /** * Created by similarface on 16/8/17. */ import org.apache.hadoop.conf.Configuration; import org. ...

  3. C++ Primer 第三章 标准库类型vector+迭代器iterator 运算

    1.vector: 标准库类型vector表示对象的集合,其中所有对象的类型都相同,集合中的每个对象都有一个与之对应的索引,索引用于访问对象.因为vector“容纳着”其他对象,所以它也常被称作容器( ...

  4. Android SQlite详解

    在项目开发中,我们或多或少都会用到数据库.在Android中,我们一般使用SQLite,因为Android在android.database.sqlite包封装了很多SQLite操作的API.我自己写 ...

  5. 关于ipxe启动的几个疑问

    关于ipxe启动的几个疑问 http://bbs.wuyou.net/forum.php?mod=viewthread&tid=373026&page=1&extra=#pid ...

  6. Element can be click when out of view

    WebDriver can't action the element when out of view Webdriver can't action the element when the elem ...

  7. 集成代码编辑器ACE的经验

    ACE是最流行的在线代码编辑器之一,在CanTK的集成开发环境GameBuilder里也使用了ACE.ACE的功能非常强大,但是由于使用方法不当,大家反映GameBuilder的代码编辑器不好用.最近 ...

  8. java 集合(Vector)不做重点

    Vector: 底层也是维护了一个Object数组,实现与ArrayList是一样的, 但其线程是安全的,效率低.除了比较老的系统,是不会用到的. 笔试题:ArrayList 和 Vector 的区别 ...

  9. fushioncharts的使用教程

    FusionCharts 是使用javascript 实现统计图表的js组件:其官网地址:http://www.fusioncharts.com.其早期版本FusionCharts Free 是基于f ...

  10. DI 之 3.3 更多DI的知识(陆)

    3.3.1  延迟初始化Bean 延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean. 配置方式很简单只需在<bean>标签上指定 " ...