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. 软件工程M1/M2总结

    也不分M1/M2了,就从头到尾的梳理一下这学期的软工课吧. 第一节课,老师就稀里哗啦说了一下这学期要怎么搞,什么个人项目啦,结对项目啦,团队项目一二啦,还要组队啊什么的,然后风风火火的组队. 个人项目 ...

  2. 结对项目junit测试用例

    题目:我们假设我们要写一个整数除法的类,并且给他写测试用例. 结对分工:滕娟负责写代码,搜集资料,整理,潘广玫负责进行测试,处理测试结果 github地址链接: https://github.com/ ...

  3. python 函数及变量作用域及装饰器decorator @详解

    一.函数及变量的作用   在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...

  4. octave基本指令1

    octave基本指令1 注释 使用: disp 输出指令 eg: >>a = pi; >>disp(sprintf('2 decimals:%0.2f'a)) 2 decima ...

  5. 『编程题全队』"Gugua"事务管理系统项目宣传文案

    一.项目简介 1.项目简介 Gugua是为了解决有事务管理需要的人群的痛苦, 他们需要 一个便利和高效的个人和团体事务管理平台,但是现有的方案并没有很好地解决这些需求,我们有独特的办法是提供跨平台的软 ...

  6. yii框架通过IP地址来使用gii

    这里使用的YII框架的版本是2.0.13 详情请参考官方文档:用Gii生成代码 使用gii的主要步骤 1.生成模型(Model Generator) 2.生成CRUD代码 注意点 1.在生成CURD代 ...

  7. spring-web-4.3.3与spring-webmvc-4.3.3的区别

    spring-web-4.3.3 http(http协议的实现类)和web包(应用,上下文,会话,cookies,过滤器等等) spring-webmvc-4.3.3 主要是一些view层的核心封装, ...

  8. HTML5的placeHolder在IE9下workaround引发的Bug(按下葫芦起了瓢)

    详见StackOverFlow的:Simple jQuery form Validation: Checking for empty .val() failing in ie9 due to plac ...

  9. Linux 改变文件属性与权限

    常用的修改文件组或文件的命令有三个:chgrp.chown.chmod. 1 chgrp 改变文件所属的用户组 改变一个文件的用户组直接以chgrp来改变即可,这个命令时change group 的简 ...

  10. 周刷题第一期总结(two sum and two numbers)

    由于深深的知道自己是事件驱动型的人,一直想补强自己的薄弱环节算法,却完全不知道从哪里入手.所以只能采用最笨的办法,刷题.从刷题中遇到问题就解决问题,最后可能多多少少也能提高一下自己的渣算法吧. 暂时的 ...