题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4126

题目意思:

给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边(可以经过可疑边新的花费构建的边),注意每次只出现一条可疑的边,n个点相互连通的最小花费的期望。

解题思路:

树形dp+MST。

先用kruskal算法找到最小生成树,并求出总花费sum.

再以枚举n个点,依次作为树根dfs,dp[i][j]表示<i,j>为最小生成树上的边,且去掉该边后,包括点i的连通块中的点集A到包括点j的连通块点集B的最小距离。

对于根节点为ro,边为<i,j>的dp[i][j]=min(以j节点为根的子树到ro的最短距离,dp[i][j]).

如下图所示:

以右边点集为子树求出左边点集中每个点作为树根时到all的最小距离。其实对于上面那条边,只用枚举ro个点就行了,但是不好确定每条边的ro集,所以枚举n个点,作为ro,然后dfs,最每条边更新一次,时间复杂度为o(n^2)可以接受。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std; #define Maxn 3300
struct Edge
{
int a,b,c;
}edge[Maxn*Maxn]; //保存边的信息 int dis[Maxn][Maxn]; //原始距离
bool hav[Maxn][Maxn]; //是否为最小生成树上的边
int fa[Maxn],dp[Maxn][Maxn];//dp[i][j]表示<i,j>为最小生成树上的边,且去掉该边后,包括点i的连通块中的点集A到包括点j的连通块点集B的最小距离。
int n,m,cnt;
ll sum; int find(int x) //并查集
{
int tmp=x;
while(x!=fa[x])
x=fa[x];
while(fa[tmp]!=x)
{
int tt=fa[tmp];
fa[tmp]=x;
tmp=tt;
}
return x;
}
bool cmp(struct Edge a,struct Edge b)
{
return a.c<b.c;
}
struct EE //构建最小生成树
{
int v;
struct EE * next;
}ee[Maxn<<1],*head[Maxn<<1]; void add(int a,int b)
{
++cnt;
ee[cnt].v=b;
ee[cnt].next=head[a];
head[a]=&ee[cnt];
} void kruskal() //克鲁斯卡尔算法求最小生成树
{
sum=0;
cnt=0;
for(int i=1;i<=m;i++)
{
int a=find(edge[i].a),b=find(edge[i].b);
if(a!=b)
{
fa[b]=edge[i].a;
sum+=edge[i].c;
hav[edge[i].a][edge[i].b]=hav[edge[i].b][edge[i].a]=true;
add(edge[i].a,edge[i].b); //建树
add(edge[i].b,edge[i].a);
}
}
}
int dfs(int ro,int fa,int cur,int dep) //表示以cur作为当前子树根中所有子树节点到总根ro的最短距离
{
struct EE * p=head[cur];
int mi=INF; if(dep!=1) //不为树根的儿子
mi=dis[cur][ro];
while(p)
{
int v=p->v;
if(v!=fa)
{
int tt=dfs(ro,cur,v,dep+1);
mi=min(mi,tt);
dp[cur][v]=dp[v][cur]=min(dp[v][cur],tt);//更新当前边
}
p=p->next;
}
return mi; } int main()
{
// printf("%d\n",INF);
while(scanf("%d%d",&n,&m)&&n+m)
{
memset(dis,INF,sizeof(dis));
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[i].a=a,edge[i].b=b,edge[i].c=c;
dis[a][b]=dis[b][a]=c;
}
sort(edge+1,edge+m+1,cmp);
for(int i=0;i<n;i++)
fa[i]=i;
memset(hav,false,sizeof(hav));
memset(head,NULL,sizeof(head));
kruskal(); memset(dp,INF,sizeof(dp));
for(int i=0;i<n;i++) //以每个点最为树根,对每条边更新n次
dfs(i,i,i,0); ll ans=0;
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(hav[a][b]) //是最小生成树上的边
{
int tt=min(dp[a][b],c); //要么用新边,要么用不是最小生成树上的边
ans=ans+sum-dis[a][b]+tt;
}
else //不是最小生成树上的边,直接用最小生成树
ans=ans+sum;
// printf("*%lf\n",ans);
}
printf("%.4f\n",ans*1.0/q);
}
return 0;
}

树形dp+MST-hdu-4126-Genghis Khan the Conqueror的更多相关文章

  1. HDU 4126 Genghis Khan the Conqueror 最小生成树+树形dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4126 Genghis Khan the Conqueror Time Limit: 10000/50 ...

  2. HDU 4126 Genghis Khan the Conqueror MST+树形dp

    题意: 给定n个点m条边的无向图. 以下m行给出边和边权 以下Q个询问. Q行每行给出一条边(一定是m条边中的一条) 表示改动边权. (数据保证改动后的边权比原先的边权大) 问:改动后的最小生成树的权 ...

  3. HDU 4126 Genghis Khan the Conqueror (树形DP+MST)

    题意:给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边 (可以经过可疑边新的花费构建的边),注意每次只出现一条可疑的边,n个 ...

  4. HDU-4126 Genghis Khan the Conqueror 树形DP+MST (好题)

    题意:给出一个n个点m条边的无向边,q次询问每次询问把一条边权值增大后问新的MST是多少,输出Sum(MST)/q. 解法:一开始想的是破圈法,后来想了想应该不行,破圈法应该只能用于加边的情况而不是修 ...

  5. 刷题总结——Genghis Khan the Conqueror (hdu4126)

    题目: Genghis Khan(成吉思汗)(1162-1227), also known by his birth name Temujin(铁木真) and temple name Taizu(元 ...

  6. UVA- 1504 - Genghis Khan the Conqueror(最小生成树-好题)

    题意: n个点,m个边,然后给出m条边的顶点和权值,其次是q次替换,每次替换一条边,给出每次替换的边的顶点和权值,然后求出这次替换的最小生成树的值; 最后要你输出:q次替换的平均值.其中n<30 ...

  7. uvalive 5834 Genghis Khan The Conqueror

    题意: 给出一个图,边是有向的,现在给出一些边的变化的信息(权值大于原本的),问经过这些变换后,MST总权值的期望,假设每次变换的概率是相等的. 思路: 每次变换的概率相等,那么就是求算术平均. 首先 ...

  8. 【Uvalive 5834】 Genghis Khan the Conqueror (生成树,最优替代边)

    [题意] 一个N个点的无向图,先生成一棵最小生成树,然后给你Q次询问,每次询问都是x,y,z的形式, 表示的意思是在原图中将x,y之间的边增大(一定是变大的)到z时,此时最小生成数的值是多少.最后求Q ...

  9. 「日常训练」 Genghis Khan the Conqueror(HDU-4126)

    题意 给定\(n\)个点和\(m\)条无向边(\(n\le 3000\)),需要将这\(n\)个点连通.但是有\(Q\)次(\(Q\le 10^4\))等概率的破坏,每次破坏会把\(m\)条边中的某条 ...

  10. 【树形DP】 HDU 2412 Party at Hali-Bula

    给出根节点(BOSS) 然后还有N-1个边  A B 由B指向A (B为A 的上司) 每次仅仅能选择这个关系中的当中一个 求最多选几个点 而且输出是不是唯一的 重点推断是否唯一: 1.若下属不去和去都 ...

随机推荐

  1. JAVAEE filter总结

    1.  为什么需要filter? filter相当于客户端和服务器端之间的一扇门,就像保安一样.作用:比如说设置字符集和权限控制等等. 2.  细节; * . 只能对post请求起作用 *  .可以使 ...

  2. 打造支持apk下载和html5缓存的 IIS(配合一个超简单的android APP使用)具体解释

    为什么要做这个看起来不靠谱的东西呢? 由于刚学android开发,还不能非常好的熟练控制android界面的编辑和操作,所以我的一个急着要的运用就改为html5版本号了,反正这个运用也是须要从serv ...

  3. Linux基本配置和管理 2 ---- Linux多命令协作----管道及重定向

    1 管道和重定向 1 在Linux中大多数命令都很简单,很少出现复杂的命令,每个命令只是实现一个简单的功能,我们可以通过组合不同的命令来实现复杂的功能 2 在Linux中几乎所有的命令返回的数据都是纯 ...

  4. 会用errno,事半功倍

    参考一 参考二 参考三 参考四 一. errno二. 把errno的数字转换成相应的文字说明1. 使用strerror函数2. 使用perror函数三. errno的线程/进程安全性附录 一. err ...

  5. iOS开发系列之触摸事件

    基础知识 三类事件中触摸事件在iOS中是最常用的事件,这里我们首先介绍触摸事件. 在下面的例子中定义一个KCImage,它继承于UIView,在KCImage中指定一个图片作为背景.定义一个视图控制器 ...

  6. Java基础知识强化之集合框架笔记09:Collection集合迭代器使用的问题探讨

    1.Collection集合迭代器使用的问题探讨: (1)问题1:能用while循环写这个程序,我能不能用for循环呢?                  可以使用for循环替代. (2)问题2:不要 ...

  7. Java设计模式04:常用设计模式之建造者模式(创建型模式)

    1. Java之建造者模式(Builder Pattern) (1)概念:       将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示. [ 构建与表示分离, 同构建不同表示 ] ...

  8. Linq101-Set

    using System; using System.Collections.Generic; using System.Linq; namespace Linq101 { class Set { / ...

  9. 自定义android精美聊天界面

    编写精美聊天界面,那就肯定要有收到的消息和发送的消息. 首先还是编写主界面,修改activity_chat.xml中的代码,如下所示: <?xml version="1.0" ...

  10. 关于Core Data的一些整理(五)

    关于Core Data的一些整理(五) 在Core Data中使用NSFetchedResultsController(以下简称VC)实现与TableView的交互,在实际中,使用VC有很多优点,其中 ...