1、前言
考得真是萎。但是我也没办法啊,这确实是想不到Prim算法好不好。。。感觉这题目还是挺神的。今天是图论专题,但是有种很明显的感觉,暴力实在不好打。
2、Test 最优检测
大概题意:有一个长度为n的0-1数列,但你并不知道是多少。你可以花费c[i][j]的代价得到[i,j]在mod 2意义下的和。求确定n个位置所有数字的最小代价。
总结:放在图论题里面让我很惊讶。。。毕竟这一眼就觉得是动态规划 = =。。伤心。。结果还没有写完,但事实证明即便写完了也会全WA,貌似这个动态规划是不可行的。接下来我就看到全场3个人都写了Prim把我吓坏!你们都是怎么搞的。。。看了代码还是觉得莫名其妙,后来在一番指点之下才明白思路。可是我还是觉得他们太神了!
题解:30分的算法应该还是有的,但是不了解;100分算法,Prim算法。首先我们发现,对于某一项区间[i,j],可以分解为[1,i-1]和[1,j]两项前缀和,将他们视为一组。也就是说,每一个代价,得到的是i-1前缀和以及j前缀和的关系,而我们要利用这些关系得到每一个数是什么当且仅当每一个前缀和都已知了。根据定义,若要已知[1,5],当且仅当已知[1,4]和[5,5],或是[1,3],[4,5],或是等等。。。这个时候可以自动脑补成一张无向图,每一对前缀和关系就相当于图中的边,并且存在权值。这样若要知道有最小代价求出所有节点,求其最小生成树即可。
代码:
------------------------------------------------------------------------------------------------------
#include<cstdio>
#define MAXN 2005
#define INF 1<<30
int min(int a,int b) { return (a<b)?a:b; }
int n,map[MAXN][MAXN],dis[MAXN],ans,vis[MAXN],minc,num;
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j++) scanf("%d",&map[i-1][j]),map[j][i-1]=map[i-1][j];
for (int i=1;i<=n;i++) dis[i]=map[0][i];
for (int i=1;i<=n;i++)
{
minc=INF;
for (int j=1;j<=n;j++)
if (!vis[j] && dis[j]<minc) minc=dis[j],num=j;
ans+=minc,vis[num]=1;
for (int j=1;j<=n;j++) if (!vis[j]) dis[j]=min(map[num][j],dis[j]);
}
printf("%d",ans);
return 0;
}
------------------------------------------------------------------------------------------------------
3、Graph 图的中心
大概题意:给出一张无向图,求图中某一点(可以在边上)到图中所有节点中距离最大者最小。
总结:首先明确了肯定是要枚举边去讨论的。题目恶心的地方就在于可以在边上,也就是说出现小数是在所难免的。想了很久思路依旧停滞在了如何选择边上的节点,最终弃疗。此题暴力感觉确实很麻烦啊,貌似正解还好写一些。
题解:先用floyd求出两两距离。枚举一条边u-v,设点i到两端的距离分别为du_i、dv_i。假设决定了最终位置,按du_i给所有点排序,一定是一个前缀从u进来,其余点从v进来。所以枚举一个前缀,得到从两边进来的最远点,取中点更新答案即可。
代码略。
4、Rain 下雨
题解:缩点相当于把边权设为0,相当于走的时候拥有一次“瞬移”的机会,把这个机会记进状态。然后一个点显然是越早到越好,跑Dijkstra即可。