dijkstra+贪心

每次加一个到起始点(首都)距离最小的点。

然后加边,这个最小点必然通过一条边和已加入的某个点相连,在这个最小点与已加入的点相连的边中,选取最短的一条边加入。

证明如下:

前提:

图G=<V,E,W>。点x到点y的最短距离为(途中可经过其它点)为D(x,y)或D(y,x),若y=1,则简写为D(1)。若点x和点y存在道路(直达),则其中最短的道路长度为W(x,y)或W(y,x)。

做法:

I.把V分成两个子集S和T。初始时,S={1},T=V-S。

II.对T中每一元素t计算D(t)(当前点t到点1的路径中,经过的点仅限为S中的点),根据D(t)值找出T中距点1最短的一结点x,写出1到x的最短路径(当前点t到点1的路径中,经过的点仅限为S中的点)的长度D(x)。在点x与S集合中的点相连的边中,选取最短的一条边加入。

III.置S为S+{x},置T为T-{x},若T为空,则停止,否则再重复2。

证明1:若x是T中具有最小D值的结点,则D(x)是从a到 x的最短距离。

假设a到x中另有一条含有T中结点的最短通路,不妨设这个通路中第一个属于T-{x}的结点是t1,于是D(x)=D(t1)+D(t1,x)>D(t1)>D(x)。矛盾,所以得证。

求D(t)的方法:

初始时,如果点t到点1存在道路,则D(t)=W(1,t),否则D(t)=无穷(一个足够大的数)。假设对T中的每一个t已计算了D值。设x是T中D值最小的一个结点,记S’=S+{x},T’=T-{x},令D’(t)表示T’中结点t的D值,则

D’(t)=min[D(t),D(x)+W(x,t)]。

证明2:求D(t)的方法正确

从点1到点t的最短路径(不包含T’中的其它结点):

情况1:它不包含T’中的其它结点,则D’(t)=D(t)。

情况2:它从点1到点x,然后点x到点t(道路),则D’(t)=D(x)+W(x,t)。

情况3:它从点1到点x,然后从点x到点t(非道路),则从点x到点t的路径中必经过一点,设为点y。可以理解为从点1到点t的最短路径为点1到点y,再点y到点t(不包含T’中的其它结点),而点1到点y的最短路径经过点x,有D(x)<D(y),但y是T中的点,x是T中新加入的点,y在x之前出现,有D(y)<=D(x),矛盾,所以情况3不可能出现。

所以根据情况1、2,D’(t)=min[D(t),D(x)+W(x,t)],得证。

证明3:在点x与S集合中的点相连的边中,选取最短的一条边加入,所耗费的代价最小。

如果点1到点x的最短路径为点1到点y(点x为最小点时的T集合(S集合的补集)中的点),点y到点x(道路),则D(x)=D(y)+D(x,y)>D(y)>D(x),矛盾,所以得证。

 #include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdbool.h> #define max_ 1000000000 int main()
{
struct node
{
long point,len;
struct node *next;
};
long n,m,dis[],add[],mindis,minadd;
long total=,i,j,a,b,c,d,dist;
bool vis[]={false};
struct node *info[],*p; scanf("%ld%ld",&n,&m);
for (i=;i<=n;i++)
info[i]=NULL; for (i=;i<=m;i++)
{
scanf("%ld%ld%ld",&a,&b,&c);
p=(struct node *) malloc (sizeof(struct node));
p->point=b;
p->len=c;
if (info[a]!=NULL)
{
p->next=info[a];
info[a]=p;
}
else
{
p->next=NULL;
info[a]=p;
}
p=(struct node *) malloc (sizeof(struct node));
p->point=a;
p->len=c;
p->next=NULL;
if (info[b]!=NULL)
{
p->next=info[b];
info[b]=p;
}
else
{
p->next=NULL;
info[b]=p;
}
}
for (i=;i<=n;i++)
{
dis[i]=max_;
add[i]=max_;
}
p=info[];
while (p)
{
dis[p->point]=p->len;
add[p->point]=p->len;
p=p->next;
}
dis[]=;
add[]=;
vis[]=true;
for (i=;i<n;i++)
{
mindis=max_;
minadd=max_;
for (j=;j<=n;j++)
if (!vis[j])
{
if (dis[j]<mindis)
{
mindis=dis[j];
minadd=add[j];
d=j;
}
else if (dis[j]==mindis && add[j]<minadd)
{
minadd=add[j];
d=j;
}
}
total+=minadd;
vis[d]=true;
//m<100000 , (x,y)对有2*m个,只要判断2*m次即可
p=info[d];
while (p)
{
if (!vis[p->point])
{
dist=mindis+p->len;
if (dist<dis[p->point])
{
dis[p->point]=dist;
add[p->point]=p->len;
}
else if (dist==dis[p->point] && p->len<add[p->point])
add[p->point]=p->len;
}
p=p->next;
}
}
printf("%ld\n",total);
return ;
}
/*
input:
5 7
5 1 2
5 4 10
1 4 7
1 2 3
2 4 6
3 2 4
4 3 5
output:
16
*/

注意不能直接开[10001][100001]的数组,会直接空间溢出,也不能开[10000][10000]的点,n=10000时不对。

拓展:

1 ≤ n ≤ 10000,1 ≤ m ≤ 100000

上述程序方法每次找最小距离的点(n个),共找n次;(x,y)对有2*m个,只要判断2*m次即可,2*m<=n*n,时间复杂度为O(n^2)。实际上n*n=100000000理应通不过,但实际上过了。

如果用堆排序,判断2*m次,则最多修改2*m次,每次修改时间复杂度为ln(k)(k为树中点的个数,k<=n),最多要操作ln(10000)*2*100000=1842068次,而不修改的堆排序最坏时间为nlnn=1151292,判断2*m次需要操作200000次,其实算的都是大概的数字,最终的操作次数不会大于算的数字*10,所以用这个方法不会超时。

另外我的一个同学想了一个方法,当求得一个点到首都1距离最短时,就把最短距离要经过的边都设为0,然后继续求其他点到首都的最短距离。

这方法不对,不能保证每个点到首都都是最短距离,其实他求的是最小生成树。假如有3个点,点1到点2的有一条长度为点5的道路,点2到点3有一条长度为3的道路,点1到点3有一条长度为6的道路,那么我们应选择点1到点3的路径来保证3到首都的距离是最短的,而不是选择点2到点3的道路。

求最小生成树(所有点都能通过边到达,边的长度之和最小)的时候,不保证两点之间的距离就是最短路径;同样的,求其他点到一个点的最短路径时,不保证这些路径之和就是最小生成树。但是这两者选取的边的数目都是n-1条(n为点的数目),如果最小生成树的边多于n-1条,则必形成环,删去环里的一条边仍能满足所有点都能通过边到达的条件;而求最短路径,用dijstra的方法,求第k个点时需要S集合中的一个点通过一条边与第k个点相连,每多一个点,最短路径就多一条边。

csp20160904解题报告的更多相关文章

  1. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  2. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  3. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  4. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  5. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

  6. 习题: codevs 2492 上帝造题的七分钟2 解题报告

    这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...

  7. 习题:codevs 1519 过路费 解题报告

    今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...

  8. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  9. LeetCode 解题报告索引

    最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中......                        ...

随机推荐

  1. RDMA技术解析

    文章出处:https://mp.weixin.qq.com/s/pW-tQR4AYr1Gtd4dpHVW7w 摘要:远程直接内存访问(即Remote Direct Memory Access)是一种直 ...

  2. spring-session-data-redis包冲突

    包冲突 spring 的包很容易冲突, 因为写软件的人在兼容性上处理的不够,一般不检测重复加载. spring-session-data-redis 引用后, 一定要把 spring-session ...

  3. 添加php的memcached扩展模块

    memcached服务直接用yum安装[root@localhost ~]# yum install memcached 然后启动memcache服务,启动多个实例[root@localhost ~] ...

  4. Android之JSON格式数据解析

    查看原文:http://blog.csdn.net/hantangsongming/article/details/42234293 JSON:JavaScript 对象表示法(JavaScript ...

  5. 《面向对象程序设计》c++第五次作业___calculator plus plus

    c++第五次作业 Calculator plusplus 代码传送门 PS:这次作业仍然orz感谢一位同学与一位学长的windows帮助,同时再次吐槽作业对Mac系统用户的不友好.(没朋友千万别用Ma ...

  6. 量产救U盘

    同事U盘不能格式化,快速格式化失败,非快速格式化也失败.就问谁有360安全软件,试试能不能格式化. 我说我有火绒,但是不知道火绒并没有格式化U盘的功能(应该没有吧,反正我找了以后没找到) 那怎么办呢? ...

  7. Ping命令的另一种使用方法

    今天实习结束休息的时候无聊,于是便想看看机房有多少机器,IP是什么,有没有什么小漏洞. 依次使用了netstat.ping.Telnet以后,不小心输入了这样一个东西 当时按下回车以后,心里想的是这样 ...

  8. Event事件2

    1,阻止默认行为: 2,事件监听 3,事件流之事件捕获 4,事件委托 阻止默认行为 浏览器中有很多默认行为,比如当点击a标签后,会发生链接的跳转.当点击鼠标的右键时, 会显示右键菜单等. 有些时候,浏 ...

  9. SQLSERVER 2014 SP1 的服务器 日志文件无法收缩的处理

    1. 公司一台服务器 日子会文件到了 100g+ 但是无法收缩 2. 根据同事的经验进行验证 dbcc loginfo 单独看改数据库的 dbcc loginfo("CWBASEGS60&q ...

  10. BOM嵌套简单写法

    WITHTREE AS( SELECT a.FItemID cfitemid,b.FItemID pfitemid FROM dbo.ICBOMChild a,dbo.ICBOM b WHERE a. ...