- 写在前面
kruskal
A - Slim Span
简单题。从小枚举起始边构成最小生成树(kruskal)求差值。最后求最小差值。https://blog.csdn.net/qiang_____0712/article/details/84529514#include<iostream>
#include <algorithm>
using namespace std;
const int maxn=105+5;
const int inf=0x3f3f3f3f;
int n,m;
int f[maxn];
struct NodE{
int u,v,w;
}node[maxn*(maxn-1)/2];
int cmp(NodE a,NodE c){
return a.w<c.w;
}
void initt(){
for(int k=1;k<=n;k++)f[k]=k;
}
int find(int x){
if(f[x]!=x)f[x]=find(f[x]);
return f[x];
}
int main()
{
while(cin>>n>>m&&(n||m)){
for(int i=1;i<=m;i++){
cin>>node[i].u>>node[i].v>>node[i].w;
}
sort(node+1,node+m+1,cmp);
int ans=inf;
for(int i=1;i<=m;i++){
initt();int coun=0;
for(int j=i;j<=m;j++){
int u=node[j].u,v=node[j].v;
u=find(u),v=find(v);
if(u!=v){
coun++;
f[u]=v;
if(coun==n-1)ans=min(ans,node[j].w-node[i].w);
}
}
}
if(ans==inf){cout<<-1<<endl;
continue;}
else cout<<ans<<endl;
continue;
}
}
B - Desert King
最有比例生成树。其实就是prime变形。思维有点难享但学会了就很容易看出。二分prime即可。 有空重新全写一遍 题解:思路:(转载)最小生成树的表达式可以这样写
∑x[i]*dis[i]-minsum>=0;(x[i]为0或者1,要求为一棵生成树)
这个题目ans<=(∑cost[i]*x[i])/(∑dis[i]x[i]).变形可得∑x[i](cost[i]-dis[i]*ans)-0>=0;cost[i]-dis[i]*ans就相当于最小生成树中的dis[i];
二分ans,check的时候跑一边prime算法看得到的最小生成树的和是否>=0,最后可得答案;
还可以采用迭代的方法;
https://www.cnblogs.com/zhangchengc919/p/5684346.html
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const double inf=1e18;
#define mem(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i ++)
#define dec(i, a, b) for(int i = a; i >= b; i --)
#define rmq_log2(x,val) while((1<<(x+1))<=val)x++;
typedef long long ll;
const int maxn=1e3+10;
const double eps=1e-5;
int n;
double cost[maxn][maxn],dis[maxn][maxn],x[maxn],y[maxn],z[maxn];
int vis[maxn];
double get_dis(int a,int b)
{
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
int prime(double x)
{
mem(vis,0);
double diss[maxn];
double sum=0;
vis[1]=1;
rep(i,1,n)diss[i]=cost[1][i]-x*dis[1][i];
rep(i,2,n){
double minn=inf;
int k=0;
rep(e,2,n) if(!vis[e]&&minn>diss[e]){
minn=diss[e];
k=e;
}
if(k==0)break;
vis[k]=1;
sum+=minn;
rep(e,2,n) if(!vis[e]&&diss[e]>cost[k][e]-x*dis[k][e]/**/)
diss[e]=cost[k][e]-x*dis[k][e];
}
if(sum>=0)return 1;
return 0;
}
int main()
{
while(cin>>n&&n){
rep(i,1,n)scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
rep(i,1,n) rep(e,i+1/**/,n) {
cost[i][e]=cost[e][i]=abs(z[i]-z[e]);
dis[i][e]=dis[e][i]=get_dis(i,e);
}
double l=0.00,r=100.0;
while(l<=r-eps){
double_t mid=(l+r)/2;
if(prime(mid))l=mid;
else r=mid;
}
printf("%.3f\n",r);
}
}
听说迭代更快,但是t了emmm就很有趣。晚点再看吧
double l=0,r=0;
while(true){
r=prime(l);
if(fabs(r-l)<1e-4)break;
l=r;
}
printf("%.3f\n",r);
C - Big Christmas Tree
这道题只要懂得(所有后代节点权重之和×边权)=(根节点到各个节点距离×点权)不懂的话用样例退一下(设几个边点就很容易看出来了)一开始看到这个题很萌比。感觉可能还是最小生成树,就是在最小生成树基础加点权,比如c这种?但是怎么计算后代节点这就把我难住了。所以去搜了题解。发现并不是想的那样,而是最短路。所以搞懂了,其实就是个djkstra半裸题。写了当模板用好了(其实有一些特盘还没完全搞懂最好重写一遍)#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
#define ll long long
const int maxn=1e5+5;
const ll inf=1e17;
struct node
{
ll dis;
int pos;
node() {}
node(ll dis,int pos):dis(dis),pos(pos) {}
bool operator< (const node& a)const
{
return dis>a.dis;
}
};
struct edge
{
int to,next;
ll z;
} e[maxn*2];//双边,无向图,所以乘以2
int head[maxn],cnt;
void add(int x,int y,ll w)// w边值
{
e[cnt].to=y;
e[cnt].z=w;
e[cnt].next=head[x];
head[x]=cnt++;
}
ll dis[maxn],vis[maxn],w[maxn];
ll ans=0;
int n,m;
void dijkstra()
{
priority_queue<node>Q;//优先队列
Q.push(node(0,1));
ans=0;//
int cnt=0;//
while(!Q.empty())
{
node cur=Q.top();
Q.pop();
if(vis[cur.pos])
continue;
cnt++;//
vis[cur.pos]=1;
ans+=dis[cur.pos]*w[cur.pos];
// cout<<dis[cur.pos]<<w[cur.pos]<<endl;
for(int v=head[cur.pos]; ~v; v=e[v].next)
{
int ne=e[v].to;
if(!vis[ne]&&dis[ne]>cur.dis+e[v].z)
{
dis[ne]=cur.dis+e[v].z;
Q.push(node(dis[ne],ne));
}
}
}
if(cnt!=n){
ans=-1;
}
}
void init()
{
for(int i=1; i<=n; i++)
{
head[i]=-1;
scanf("%lld",&w[i]);
dis[i]=inf,vis[i]=0;
}
cnt=0;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
int x,y;
ll z;
init();
for(int i=1; i<=m; i++)
{
scanf("%d%d%lld",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
if(n==0||n==1){
printf("0\n");
continue;
}
dis[1]=0;
dijkstra();
if(ans!=-1){
printf("%lld\n",ans);
}
else printf("No Answer\n");
}
return 0;
}
POJ 3463 D - Sightseeing
lalala这道题又是一道一开始完全不会后面就会了的题。。。一开始没读懂题,以为他要求最短路和经过点数比最短路点多的路后来看了下题解才发现是求最短路和次短路。看到一个前向星和djk的题解。也放在这里以下https://blog.csdn.net/sdj222555/article/details/7690694解释得挺清楚了https://www.cnblogs.com/north_dragon/archive/2010/06/05/1752419.html。
写了一个前向星和优先队列的。鉴于我是个手残,根据模板改的。没想到居然没错算是理解了吧开心接下来将下细节。 两个注意点:
1.由于要记录最短路次短路所以d[][]数组和vis[][]数组开二维用来表示是最短路还是次短路。edge记录边数
2.比较时考虑四种情况
1)新值小于最短路径长:更新最短路径长,计数;次短路径长,计数 2)新值等于最短路径长:更新最短路径计数 3)新值大于最短路径长,小于次短路径长:更新次短路径长,计数 4)新值等于次短路径长:更新次短路径计数
#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
#define ll long long
const int maxn=1e5+5;
const ll inf=1e17;
struct node
{
ll dis;
int pos;
int flag;
node() {}
node(ll dis,int pos,int flag):dis(dis),pos(pos),flag(flag) {}
bool operator< (const node& a)const
{
return dis>a.dis;
}
};
struct Edge
{
int to,next;
ll z;
} e[maxn*2];//双边,无向图,所以乘以2
int head[maxn],cnt;
void add(int x,int y,ll w)// w边值
{
e[cnt].to=y;
e[cnt].z=w;
e[cnt].next=head[x];
head[x]=cnt++;
}
ll dis[maxn][2],edge[maxn][2],vis[maxn][2];
int n,m;
int dijkstra(int s,int f)
{
priority_queue<node>Q;//优先队列
dis[s][0]=0;
Q.push(node(0,s,0));
while(!Q.empty())
{
node cur=Q.top();
Q.pop();
if(vis[cur.pos][cur.flag])
continue;
vis[cur.pos][cur.flag]=1;
for(int v=head[cur.pos]; ~v; v=e[v].next)
{
int ne=e[v].to;
int w=e[v].z;
if(dis[ne][0]>cur.dis+w)
{
dis[ne][1]=dis[ne][0];
edge[ne][1]=edge[ne][0];
dis[ne][0]=cur.dis+w;
edge[ne][0]=edge[cur.pos][cur.flag];
}
else if(dis[ne][0]==cur.dis+w){
edge[ne][0]+=edge[cur.pos][cur.flag];
}
else if(dis[ne][1]>cur.dis+w){
dis[ne][1]=cur.dis+w;
edge[ne][1]=edge[cur.pos][cur.flag];
}
else if(dis[ne][1]==cur.dis+w){
edge[ne][1]+=edge[cur.pos][cur.flag];
}
Q.push(node(dis[ne][0],ne,0));
Q.push(node(dis[ne][1],ne,1));
}
}
int ans = 0;
if(dis[f][1] == dis[f][0] + 1) ans = edge[f][0]+edge[f][1];
else ans = edge[f][0];
return ans;
}
void init()
{
for(int i=1; i<=n; i++)
{
head[i]=-1;
dis[i][0]=dis[i][1]=inf,vis[i][0]=vis[i][1]=0;
edge[i][0]=edge[i][1]=1;
}
cnt=0;
}
int main()
{
int s, t, T, x, y, w;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &y, &w);
add(x, y, w);
}
scanf("%d%d", &s, &t);
printf("%d\n", dijkstra(s, t));
}
return 0;
}
E - Remmarguts' Date
学会新的算法A*。具体详情点击这里。简单来讲。dijk可以很好找到各个点的最短距离而A*则正确快速找到路径。如果还不理解可以从优先队列来看。dij的优先队列存在为了找到已有路径最短更新其他店。而A*优先队列(以g+h大小排序)存在是为了找到目标点最优的道路,然后更新这个点之后可能的最有道路。
这道题题意:找到第k小的最短路径长。(题目有点难懂) 思路:dij求个点h长,实时更新g根据f=g+h排序找到最优路径。设变量控制第几大。
https://www.cnblogs.com/Missa/archive/2012/08/30/2664668.html
#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn=1010;
const int inf=1e17;
int dis[maxn],vis[maxn];
struct node
{
int diss;
int pos;
node() {}
node(int diss,int pos):diss(diss),pos(pos) {}
bool operator< (const node& a)const
{
return diss+dis[pos]>a.diss+dis[a.pos];
}
};
struct Edge
{
int u,v,next,nextt;
int z;
} e[100005];//双边,无向图,所以乘以2
int head[maxn],head1[maxn],cnt;
int ee,k;
void add(int x,int y,int w)// w边值
{
e[cnt].u=x;
e[cnt].v=y;
e[cnt].z=w;
e[cnt].next=head[x];head[x]=cnt;
e[cnt].nextt=head1[y];head1[y]=cnt++;
}
int n,m;int sr;
int dijkstra(int s)
{
priority_queue<node>Q;//优先队列
dis[s]=0;
Q.push(node(0,s));
while(!Q.empty())
{
node cur=Q.top();
Q.pop();
if(vis[cur.pos])
continue;
vis[cur.pos]=1;
for(int v=head1[cur.pos]; ~v; v=e[v].nextt)
{
int ne=e[v].u;
int w=e[v].z;
if(dis[ne]>cur.diss+w)
{
dis[ne]=cur.diss+w;
Q.push(node(dis[ne],ne));
}
}
}
}
void init()
{
for(int i=1; i<=n; i++)
{
head[i]=head1[i]=-1;
dis[i]=inf,vis[i]=0;
}
cnt=0;
}
int a_star(int s)
{
priority_queue<node>Q;//优先队列
Q.push(node(0,s));
while(!Q.empty())
{
node cur=Q.top();
Q.pop();
if(cur.pos==ee){
if(k>1)k--;
else return cur.diss;
}
for(int v=head[cur.pos]; ~v; v=e[v].next)
{
int ne=e[v].v;
int w=e[v].z;
Q.push(node(cur.diss+w,ne));
}
}
return -1;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
int x,y,w;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &x, &y, &w);
add(x, y, w);
}
scanf("%d%d%d",&sr,&ee,&k);
dijkstra(ee);
if(dis[sr]==inf){
cout<<-1<<endl;
continue;
}
if(sr==ee)k++;
cout<<a_star(sr)<<endl;
}
return 0;
}
https://blog.csdn.net/qq_41552508/article/details/81707295
思考:如果用djk写呢?
(转)求第K短路,我们先回忆一下Dijkstra求最短路的方法,就是用优先队列bfs对整张图进行遍历,当一个点被第一次取出时,此时的距离便是该点的最短距离。
由此,我们便得知了求第K短路的方法。即对于任意顶点 i ,当顶点 i 被第K次取出时,此时的距离便是该点的第K短路。
但是,如果直接用优先队列的bfs去求第K短路,无疑会造成大量的冗余搜索。因此我们需要用A*算法,即启发式算法进行优化。
启发式算法的核心:
1.设立一个估价函数,对于每一个状态,估价函数的值就是该状态到目标状态所需的花费。注意这个花费必须小于真实花费,否则程序会出错。
2.对每一次搜索到的状态,我们定义该状态的值为“该状态的花费”+“该状态到目标状态所需的值”,即估价函数在该状态下的值。
3.然后我们每次取出状态值最小的点进行bfs,即可优化搜索。
由此,A*,即是改变bfs的搜索方向,使得我们更快地搜到目标值。