题目大意:

给定无向图,让前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 ;
}

最新文章

  1. CloudNotes之桌面客户端篇:插件系统的实现
  2. Entity Framework 实体框架的形成之旅--实体框架的开发的几个经验总结
  3. UCenter整合登陆时出现’Authorization has expired’错误(2014-03-13记)
  4. Mule ESB 社区版 企业版 资源下载 包含3.5和3.6
  5. 黑客是怎样绕过WAF之三重防护绕过讲解
  6. Node.js、Ionic、Cordova、AngualrJS安装
  7. 查询表达式和LINQ to Objects
  8. R语言do.call 函数用法详解
  9. Android源码解析——Toast
  10. 开源:ASP.NET Aries 开发框架(已支持.NET Core)
  11. RDIFramework.NET V3.3 Web版新增日程管理功能模块
  12. vue的动态路由(登录之后拿到动态路由通过addRouters()动态添加路由)
  13. Python Django对接企业微信第三方服务回调验证的一些坑
  14. Key Technologies Primer 读书笔记,翻译 --- Struct 学习 1
  15. c# 遍历类中的方法名称
  16. [leetcode]Median of Two Sorted Arrays @ Python
  17. 精伦盒子H1,插上USB,找不到对应的文件路径
  18. [html] 回到页首
  19. Day7 访问权限
  20. Linux 文件基本属性(转)

热门文章

  1. Android版本升级同时Sqlite数据库的升级及之前数据的保留
  2. opencl初探-sobel检测
  3. matlab cross 3*1 向量叉乘
  4. 深入理解JVM-3垃圾收集器与内存分配策略
  5. java 多线程7(线程的停止)
  6. js文字上下滚动代码
  7. jmeter笔记3
  8. DOM事件流
  9. Tiny语法分析器(递归下降分析法实现)
  10. struts2 mybatis spring hibernate 框架 pom.xml配置 下载地址