基础图论
来源:互联网 发布:linux编程手册 pdf 编辑:程序博客网 时间:2024/06/06 03:17
题目链接
HDU - 4463 Outlets
题意:给定一些点的坐标,有两个点必须相连,求这个图的最小生成树
分析:算出每两个点的坐标,然后直接把那两个点相连,再跑一边kruskal。
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int maxn = 55;struct vertex{int x,y;};vertex ver[maxn];struct edge{ int from,to; double dis; bool operator < (const edge x)const {return dis<x.dis;}};edge e[maxn*maxn];int pre[maxn];int Find(int k){ if(pre[k] == k)return k; else return pre[k] = Find(pre[k]);}int ni,app;int spe[2][2];double distance(int x1,int y1,int x2,int y2){ return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));}int main(){ int n; while(~scanf("%d",&n) && n != 0) { scanf("%d%d",&ni,&app); for(int i = 0; i <= n; i++) pre[i]=i; for(int i = 1; i <= n; i++) { scanf("%d%d",&ver[i].x,&ver[i].y); if(i == ni) spe[0][0] = ver[i].x,spe[0][1] = ver[i].y; if(i == app) spe[1][0] = ver[i].x,spe[1][1] = ver[i].y; } int k=0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n;j++) { if(j == i) continue; e[k].from = i; e[k].to = j; e[k++].dis = distance(ver[i].x,ver[i].y,ver[j].x,ver[j].y); } } sort(e,e+k); double dis = distance(spe[0][0],spe[0][1],spe[1][0],spe[1][1]); pre[app] = ni; for(int i = 0; i < k; i++) { int x = Find(e[i].from) , y = Find(e[i].to); if(x != y) { pre[y] = x; dis += e[i].dis; } } printf("%.2lf\n",dis); } return 0;}
HDU - 1863 畅通工程
题意:中文题。
思路:kruskal裸题
代码:
#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 105;const int maxm = 10010;const int inf = 0x3f3f3f3f;int m,n;int f[maxn];int edgenum = 0;struct node{ int from,to,w; node(){} node(int a,int b,int c) { from = a; to = b; w = c; }}edge[maxm];bool cmp(node a,node b){ return a.w < b.w;}int getfather(int i){ if(f[i] == i) return i; else return f[i] = getfather(f[i]);}int main(){ while(~scanf("%d%d",&m,&n) && m) { edgenum = 0; for(int i = 0; i < maxn ; i++) f[i] = i; for(int i = 0; i < m; i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); edge[edgenum++] = node(a,b,c); } sort(edge,edge+edgenum,cmp); int num = 0; int ans = 0; for(int i = 0; i < edgenum ; i++) { int a = getfather(edge[i].from),b = getfather(edge[i].to); if(a != b) { f[a] = b; num ++; ans += edge[i].w; if(num == n-1) break; } } num == (n-1)?printf("%d\n",ans):printf("?\n"); } return 0;}
HDU - 2544 最短路
题意:中文题
思路:最短路裸题,求出从1->N的最短路,dijkstra
代码:
//from : 1610300124#include <stdio.h>#include <string.h>const int maxn=105,inf=10000000;int d[maxn],w[maxn][maxn],vis[maxn],m,n;void Dijkstra(int src){ for(int i=1; i<=n; ++i) d[i] = inf; d[src] = 0; memset(vis, 0, sizeof(vis)); for(int i=1; i<=n; ++i) { int u=-1; for(int j=1; j<=n; ++j) if(!vis[j]) if(u==-1 || d[j]<d[u]) u=j; vis[u] = 1; for(int j=1; j<=n; ++j)if(!vis[j]) { int tmp = d[u] + w[u][j]; if(tmp<d[j]) d[j] = tmp; } } printf("%d\n",d[n]);}int main(){ int a,b,c; while(~scanf("%d%d",&n,&m)) { if(m==0&&n==0) break; for(int i=1;i<=n;++i) { w[i][i]=inf; for(int j=i+1;j<=n;++j) w[i][j]=w[j][i]=inf; } for(int i=0;i<m;++i) { scanf("%d%d%d",&a,&b,&c); w[a][b]=w[b][a] = c; } Dijkstra(1); } return 0;}
POJ - 1724 ROADS
题意: 求出从1到N的满足钱数<=K的情况下的最短路
思路: 看到这里的大一同学可能会发现我讲的时候讲错了哈,这里是优先长度排序然后再是钱数,(这里指的是出堆的顺序),其实这题会发现dijkstra的堆优化和bfs挺像的,只不过一般bfs的优先级(也就是出队顺序)是步数,而dijkstra优先级(出堆顺序)是距离。对于这一题则还有一个限制,就是总钱数,所以每次让一个点出堆,然后取更新别的值时,入堆还得判断一下能否满足到这个点的花费<=K,这样如果N点第一次出堆的时候,一定是到这个点距离最短的时候,而且满足花费<=K(是不是和bfs想法特别像)
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<vector>#include<queue>using namespace std;#define maxn 105#define INF 0x3f3f3f3fstruct edge{ int to,len,mon; edge(int a,int b,int c): to(a),len(b),mon(c){} bool operator< (const edge x)const { if(len != x.len) return len > x.len; else return mon>x.mon; }};vector<edge> V[maxn];priority_queue<edge> pq;int K,N,R;int bfs(int n,int startlen,int startmon){ pq.push(edge(n,startlen,startmon)); while(!pq.empty()) { edge temp = pq.top(); if(temp.to == N) return temp.len; pq.pop(); for(int i=0;i < V[temp.to].size(); i++) { edge & change = V[temp.to][i]; if( change.mon+temp.mon <= K ) pq.push(edge(change.to,temp.len+change.len,change.mon+temp.mon)); } } return -1;}int main(){ int from,to,len,mon; scanf("%d%d%d",&K,&N,&R); for(int i = 0; i < R; i++) { scanf("%d%d%d%d",&from,&to,&len,&mon); V[from].push_back(edge(to,len,mon)); } printf("%d\n",bfs(1,0,0)); return 0;}
POJ - 1860 Currency Exchange
题意:给出两种货币之间的汇率和手续费,问如果从货币S出发,通过一些厉害的Currency exchange 后能否赚钱。
思路:这里我们可以发现,其实这道题求的是有无正环,如果从s出发有正环的话,说明可以沿着这个正环无限赚钱,然后回到起点s的时候是会大于原来的值的。想一想bellman是可以判断负环的,那这里我们可以通过变换dis[i]的初始值,然后让bellman去判断有无正环。就是在第n次循环时,如果还能松弛,说明有正环。
代码:
//from : caijichang#include<iostream>#include<cstring>#include<cstdio>using namespace std;int n,m,s;double v;double rate1,rate2,cost1,cost2;struct change{ int begin,end; double rate,cost;}num[250]; int k;void input(int a,int b,double c,double d){ num[k].begin=a; num[k].end=b; num[k].rate=c; num[k].cost=d; k++;}double dis[110];bool bellman_Ford(){ bool flag; memset(dis,0,sizeof(dis)); dis[s]=v; for(int i=1;i<=n;i++) { flag=true; for(int j=0;j<k;j++) { if(dis[num[j].end]<(dis[num[j].begin]-num[j].cost)*num[j].rate) { flag=false; dis[num[j].end]=(dis[num[j].begin]-num[j].cost)*num[j].rate; } } if(flag) return false; } return true;}int main(){ scanf("%d%d%d%lf",&n,&m,&s,&v); int begin,end; int k=0; for(int i=0;i<m;i++) { scanf("%d%d%lf%lf%lf%lf",&begin,&end,&rate1,&cost1,&rate2,&cost2); input(begin,end,rate1,cost1); input(end,begin,rate2,cost2); } if(bellman_Ford()) printf("YES"); else printf("NO"); return 0;}
POJ - 3159 Candies (写的人有点少,过两个星期再写题解~)
HDU - 1875 畅通工程再续
题意:这个也是中文题
思路:在满足条件的位置建桥连边,然后跑一边kruskal
代码:
//from : wangziwei1998#include <iostream>#include<algorithm>#include<stdio.h>#include<cmath>using namespace std;#define max 100010int father[max];struct Node{ int start,endi; double dis;};struct point{ int x,y;};point p[105];//岛的坐标Node road[max];bool cmp(Node x,Node y){ return x.dis<y.dis;}int finda(int x)//x点找它的祖先{ if(father[x]!=x) father[x]=finda(father[x]); return father[x];}void merges(int x,int y) //把两个点的祖先合并{ int a=finda(x); int b=finda(y); if(a!=b) { father[b]=a; }}int main(){ // freopen("E:\\file.txt","r",stdin); int n; int m; scanf("%d",&n); while(n--) { int i,j,k=0; double sum=0.0; int num=0; scanf("%d",&m); for(i=0;i<m;i++) scanf("%d%d",&p[i].x,&p[i].y); for(i=0;i<m;i++) { for(j=i+1;j<m;j++) { road[k].start=i; road[k].endi=j; road[k].dis=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)); k++; } } // cout<<k<<endl; for(i=0;i<=max;i++) father[i]=i; //把他们的父节点初始化 //输入各个数据 sort(road,road+k,cmp); for(i=0;i<k;i++) { if((road[i].dis>=10)&&(road[i].dis<=1000)) { if(finda(road[i].start)!=finda(road[i].endi)) { merges(road[i].start,road[i].endi); num=num+1; sum=sum+road[i].dis; } if(num==m-1) break; } } if(num == m-1) printf("%.1lf\n",sum*100); else printf("Oh!\n"); } return 0;}
HDU - 1879 继续畅通工程
题意:中文题~
思路:由于有些路已经修好了,所以我们直接把这些路的w看作0,然后跑kruskal就可以了。
代码:
//from : 742962178#include<iostream>#include<algorithm>#include<cstdio>#include<cstring>using namespace std;const int maxn = 15000;int f[maxn];struct node{ int front, back, val; node() {} node(int a, int b, int c) :front(a), back(b), val(c) {}};node edge[maxn];int find(int n){ if (f[n] == n)return n; else f[n] = find(f[n]);}int main(){ int T = 0; while (scanf("%d", &T), T) { int te = 0; for (int i = 0; i < maxn; i++) f[i] = i; for (int i = 0; i < (T*(T-1))/2; i++) { int a = 0, b = 0, c = 0, d = 0; scanf("%d%d%d%d", &a, &b, &c, &d); if (d)c = 0; edge[te++] = node(a, b, c); } sort(edge, edge + te, [](const node& a, const node& b) {return a.val < b.val; }); int num = 0; int ans = 0; for (int i = 0; i < te; i++) { int a = find(edge[i].front); int b = find(edge[i].back); if (a != b) { f[a] = b; num++; ans += edge[i].val; if (num == T - 1)break; } } printf("%d\n", ans); } return 0;}
HDU - 2489 Minimal Ratio Tree
题意:给一些点的权值,给一些边的权值,从n个点里选定m个点,m-1条边,使得这些边连通这些点,并且边权值/点权值最小。
思路:这里我们先看一下范围啊,点数n<=15,而且我们可以发现,选定点之后,点权值就可以确定了,
我们只需要使连这m个点的边权值最小就可以的,也就是求这m个点的最小生成树。
我这里枚举是使用的按位枚举,也就是使用一个二进制数字,第i位为0表示不取这个点,1表示取这个点,选定点之后就可以跑最小生成树了。
代码:
#include <cstdio>#include <iostream>#include <algorithm>using namespace std;const int maxn = 40,inf = 0x3f3f3f3f;struct node{ int from,to,w; node(){} node(int a,int b,int c) {from = a; to = b; w = c;}}edge[maxn*maxn];int value[maxn];int data[maxn][maxn];int n,m,edgenum;bool cmp(node a,node b){ return a.w < b.w;}int f[maxn],vis[maxn];int getf(int i){ return i==f[i]?i:f[i]=getf(f[i]); }double kruskal(int sta){ for(int i = 0; i < maxn; i++) f[i] = i,vis[i] = 0; int vw = 0; int th = 1; while(sta){if(sta&1)vis[th]=1; th++;sta=(sta>>1);} int num = 0,ew = 0; for(int i = 0; i < edgenum; i++) { int u = edge[i].from,v = edge[i].to; if(!vis[u]|| !vis[v]) continue; int x = getf(u),y = getf(v); if(x!=y) { f[x] = y; num++; ew += edge[i].w; if(num == m-1) break; } } for(int i = 1; i <= n; i++) if(vis[i]) vw += value[i]; if(num != m-1) return inf; else { double res = (double)ew/vw; return res; }}int main(){ while(~scanf("%d%d",&n,&m) && n+m) { for(int i = 1; i <= n; i++) scanf("%d",&value[i]); edgenum = 0; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d",&data[i][j]); if( i != j) edge[edgenum++] = node(i,j,data[i][j]); } sort(edge,edge+edgenum,cmp); double ansa = inf; int anssta = 0; for(int i = 1; i < (1<<n) ; i++) { int k = i,ti = 0; while(k) {if(k&1) ti++; k = (k>>1);} if(ti != m)continue; else { double w = kruskal(i); if(w < ansa) anssta = i,ansa = w; } } int out = 1,flag = 0; while(anssta) { if(anssta&1) { flag ? printf(" %d",out):printf("%d",out); flag = 1; } out++; anssta = (anssta >> 1); } cout<<endl; } return 0;}
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 数据结构:图论基础
- 图论基础UVA10054
- 图论基础UVA11624
- 图论基础UVA10047
- 图论算法基础
- 图论基础算法
- 图论基础
- 图论基础
- 图论基础
- 图论基础
- 系统吞吐量、TPS(QPS)、用户并发量、性能测试概念和公式
- Vue.directive 自定义指令
- C3P0连接池配置
- 顶级登录页面html
- 前端框架Ext学习之框架搭建
- 基础图论
- ASP .NET – TextBox 控件
- 【C++数据结构与算法】学习随笔二
- vue分页组件编写
- Shell 从日志文件中选择时间段内的日志输出到另一个文件
- iOS之NSProcessInfo的使用
- java基础-----集合 set
- 2015多校第8场 HDU 5385 贪心,最小生成树
- win8升级win10后的windows.old怎么删除