1、模板题 我是用prim搞得 给出每点坐标求最小生成树
hdu1162Eddy‘s picture 最小生成树
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; int flag1=0; double sum; double arr_list[110][110]; struct Edge { int point; double lowcost; int flag; }; Edge edge[12100]; struct Point { double x,y; }point[110]; double prim(int n) { int i,j,k=1,flag; double min,sum2=0; j=1; for(i=1;i<=n;i++) { if(i!=j) { edge[i].point=i; edge[i].lowcost=arr_list[j][i]; edge[i].flag=0; } } edge[j].lowcost=0; edge[j].flag=1; for(i=2;i<=n;i++) { k=1; min=65535; flag=0; for(j=2;j<=n;j++) { if(edge[j].flag==0&&edge[j].lowcost<min) { k=j; min=edge[j].lowcost; flag=1; } } if(!flag) return -1; sum2+=min; edge[k].flag=1; for(j=2;j<=n;j++) { if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost) { edge[j].point=k; edge[j].lowcost=arr_list[k][j]; } } } return sum2; } int main() { // freopen("cin.txt","r",stdin); int n; while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) { cin>>point[i].x>>point[i].y; arr_list[i][i]=65535; } for(int i=1;i<n;i++) { for(int j=i+1;j<=n;j++) { arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2)); arr_list[j][i]=arr_list[i][j]; //cout<<arr_list[i][j]<<endl; } } sum=prim(n); printf("%.2lf\n",sum); } return 0; }
2、模板题 floyd最短路相加改成相乘
hdu1596find the safest road最短路floyd
#include <iostream> #include<cstdio> using namespace std; double dist[1005][1005]; int main() { // freopen("cin.txt","r",stdin); int n,q,a,b; while(~scanf("%d",&n)) { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { scanf("%lf",&dist[i][j]); } } for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { for(int k=0;k<n;k++) { if(dist[j][i]*dist[i][k]>dist[j][k]) dist[j][k]=dist[j][i]*dist[i][k]; } } } scanf("%d",&q); while(q--) { scanf("%d%d",&a,&b); a--;b--; if(dist[a][b]>0.000001) printf("%.3lf\n",dist[a][b]); else puts("What a pity!"); } } return 0; }
3、稍微转弯的prim
poj2349Arctic Network最小生成树
题意:p个哨所间只有s个卫星通道,但每个哨所都有无线发射器,无线发射的功率随长度增加而增加但是每个都一样,问最小长度?
我们知道prim算法中的步骤是每次加入最小的边,但是并没有将加入的边存储下来,加一个数组存储下来,倒序排序,前s个边用卫星通道就好啦
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int flag1=0; int n,s,p; double sum; double arr_list[504][504]; double d[503]; struct Edge { int point; double lowcost; int flag; }; Edge edge[505]; bool cmp(double a,double b) { return a>b; } struct Point { double x,y; }point[502]; void prim(int p) { int i,j,k=1; double min,sum2=0; j=1; for(i=1;i<=p;i++) { if(i!=j) { edge[i].point=j; edge[i].lowcost=arr_list[j][i]; edge[i].flag=0; } } edge[j].lowcost=0; edge[j].flag=1; int l=0; for(i=1;i<p;i++) { min=65535000; for(j=2;j<=p;j++) { if(edge[j].flag==0&&edge[j].lowcost<min) { k=j; min=edge[j].lowcost; } } d[l++]=arr_list[k][edge[k].point]; //sum2+=min; edge[k].flag=1; for(j=2;j<=p;j++) { if(edge[j].flag==0&&arr_list[k][j]<edge[j].lowcost) { edge[j].point=k; edge[j].lowcost=arr_list[k][j]; } } } } int main() { // freopen("cin.txt","r",stdin); cin>>n; while(n--) { cin>>s>>p; for(int i=1;i<=p;i++) { cin>>point[i].x>>point[i].y; arr_list[i][i]=65535000; } for(int i=1;i<p;i++) { for(int j=i+1;j<=p;j++) { arr_list[i][j]=sqrt(pow((point[i].x-point[j].x),2)+pow((point[i].y-point[j].y),2)); arr_list[j][i]=arr_list[i][j]; //cout<<arr_list[i][j]<<endl; } } prim(p); sort(d,d+p-1,cmp); cout.setf(ios::fixed);//保留两位小数 cout.precision(2); cout<<d[s-1]<<endl; } return 0; }
4、多源点单汇点 之前都是单源最短路,如果是多源单汇点的话也是一样的
hdu2680Choose the best route dijkstra
#include <iostream> #include<cstdio> #include<cstring> #define MaxN 10010 #define MaxInt 2000000 using namespace std; int map[MaxN][MaxN],dist[MaxN],start; bool mark[MaxN]; int n,m,s,p,q,t,w,b,end,min1,minn,ww; void dijkstra(int s) { memset(mark,0,sizeof(mark)); for(int i=1;i<=n;i++) dist[i]=map[s][i]; mark[s]=1;dist[s]=0; int k; for(int i=1;i<n;i++) { minn=MaxInt; for(int j=1;j<=n;j++) { if(!mark[j]&&minn>dist[j]) { k=j; minn=dist[j]; } } if(minn==MaxInt) break; mark[k]=1; for(int j=1;j<=n;j++) { if(!mark[j]&&dist[j]>dist[k]+map[k][j]) { dist[j]=dist[k]+map[k][j]; } } } } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d%d",&n,&m,&s)) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { map[i][j]=MaxInt; } } for(int i=0;i<m;i++) { scanf("%d%d%d",&p,&q,&t); if(t<map[q][p]) map[q][p]=t; } dijkstra(s); min1=MaxInt; scanf("%d",&w); while(w--) { scanf("%d",&ww); if(min1>dist[ww]) min1=dist[ww]; } if(min1!=MaxInt) printf("%d\n",min1); else printf("-1\n"); } return 0; }
5、限定必须连上给定的两点 这是一个关于最小生成树的原理应用,也是以签到题的姿态出现的区域赛题
HDU 4463 Outlets 最小生成树Kr~
既然要求他俩必须先连,那就先连上,逐渐选择最小的边加入边集中就ok啦
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=60*60; struct point { int x,y; double dis; point(){} point(int _x,int _y,double _d):x(_x),y(_y),dis(_d){} bool operator <(const struct point &tmp)const{ return this->dis<tmp.dis; } }p[maxn]; point pp[100]; int par[60]; void init(){ for(int i=0;i<60;i++) par[i]=i; } int find_par(int x){ if(x!=par[x]) return par[x]=find_par(par[x]); return par[x]; } bool Union(int x,int y){ x=find_par(x); y=find_par(y); if(x!=y){ par[y]=x; return true; } return false; } double calu(point a,point b){ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main() { // freopen("cin.txt","r",stdin); int n,p1,q1; while(~scanf("%d",&n)&&n) { init(); scanf("%d%d",&p1,&q1); int cnt=0; for(int i=1;i<=n;i++) { int x,y; scanf("%d%d",&pp[i].x,&pp[i].y); for(int j=1;j<i;j++) { double d=calu(pp[i],pp[j]); p[cnt++]=point(i,j,d); } } double ans=0; Union(p1,q1); ans+=calu(pp[p1],pp[q1]); sort(p,p+cnt); for(int i=0;i<cnt;i++) { if(Union(p[i].x,p[i].y)) ans+=p[i].dis; } printf("%.2lf\n",ans); } }
6、floyd最小环 裸题 前一个是输出路径
poj1734Sightseeing trip floyd最小环
hdu1599 find the mincost route
#include<iostream> #include<cstring> #include<cstdlib> #include<queue> #include<cstdio> #include<climits> #include<algorithm> using namespace std; const int N=111; const int INF=0xffffff; int min_loop; int num; int map[N][N],dist[N][N],pre[N][N]; int path[N]; int n,m; void dfs(int i,int j) { int k=pre[i][j]; if(k==0) { path[num++]=j; return; } dfs(i,k); dfs(k,j); } void Floyd() { min_loop=INF; memset(pre,0,sizeof(pre)); for(int k=1; k<=n; k++) { for(int i=1; i<k; i++) { for(int j=i+1; j<k; j++) { if(dist[i][j]+map[i][k]+map[k][j]<min_loop) { min_loop=dist[i][j]+map[i][k]+map[k][j]; num=0; path[num++]=i; dfs(i,j); path[num++]=k; } } } for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(dist[i][k]+dist[k][j]<dist[i][j]) { dist[i][j]=dist[i][k]+dist[k][j]; pre[i][j]=k; } } } } } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { for(int i=1; i<=n; i++) { for(int j=i+1; j<=n; j++) map[i][j]=map[j][i]=dist[i][j]=dist[j][i]=INF; map[i][i]=dist[i][i]=0; } for(int i=0; i<m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); if(w<map[u][v]) { map[u][v]=map[v][u]=w; dist[u][v]=dist[v][u]=w; } } Floyd(); if(min_loop==INF) puts("No solution."); else { for(int i=0; i<num-1; i++) printf("%d ",path[i]); printf("%d\n",path[num-1]); //printf("%d\n",min_loop); } } return 0; }
7、单向最短路来回最短距离
POJ 3268 Silver Cow Party
虽说是来回都得求值,还记不记得之前的“孙大神的山峰序列” 一样的道理,从两边求完之后遍历数组求和最小即为答案
#include<iostream> #include<cstring> #include<cstdlib> #include<queue> #include<cstdio> using namespace std; int n,m,x; const int MAXINT=0x3f3f3f3f; const int MAXNUM=1100; int dist[MAXNUM]; int A[MAXNUM][MAXNUM]; void Dijkstra(int v0) { bool S[MAXNUM];// 判断是否已存入该点到S集合中 for(int i=1; i<=n; ++i) { dist[i]=A[v0][i]; // printf("%d ",dist[i]); S[i]=false; // 初始都未用过该点 } // dist[v0] = 0; S[v0]=true; for(int i=2; i<=n; i++) { int mindist = MAXINT; int u = -1;// 找出当前未使用的点j的dist[j]最小值 for(int j=1; j<=n; ++j) if((!S[j]) && dist[j]<mindist) { u = j;// u保存当前邻接点中距离最小的点的号码 mindist = dist[j]; //printf("%d ",mindist); } S[u] = true; for(int j=1; j<=n; j++) if((!S[j]) && A[u][j]<MAXINT) { if(dist[u] + A[u][j] < dist[j]) //在通过新加入的u点路径找到离v0点更短的路径 { dist[j] = dist[u] + A[u][j]; //更新dis //记录前驱顶点 } } } return; } int main() { //freopen("cin.txt","r",stdin); while(~scanf("%d%d%d",&n,&m,&x)) { for(int i=1; i<=n; i++) { for(int j=i+1; j<=n; j++) A[i][j]=A[j][i]=MAXINT; } for(int i=0; i<m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); A[u][v]=w; //printf("%d %d %d\n",u,v,A[u][v]); } Dijkstra(x); int a1[MAXNUM],a2[MAXNUM]; for(int i=1;i<=n;i++) a1[i]=dist[i];//printf("%d ",a1[i]); // puts(""); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { int tmp; tmp=A[i][j]; A[i][j]=A[j][i]; A[j][i]=tmp; } Dijkstra(x); for(int i=1;i<=n;i++) a2[i]=dist[i]; int minn=0; for(int i=1;i<=n;i++) { minn=max(minn,a1[i]+a2[i]); } printf("%d\n",minn); } return 0; }
8、换货币,给出汇率,问是否可以通过一系列的折腾使得手中的钱增多,其实也不容想到是用floyd最短路的方法写== dist[i][i]>1.0即为钱数增加
POJ 2240 Arbitrage floyd
#include <iostream> #include <cstdio> #include<map> #include <string.h> using namespace std; map<string,int>mat; double dist[100][100]; int n,m; void Floyd() { for(int k=1;k<=n;k++) for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if(dist[i][k]*dist[k][j]>dist[i][j]) { dist[i][j]=dist[i][k]*dist[k][j]; // pre[i][j]=k; } } } } int main() { // freopen("cin.txt","r",stdin); map<string,int>mat; int cnt=0; double w; string str,str1,str2; while(~scanf("%d",&n)&&n) { //mat.clear(); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) { if(i==j) dist[i][j]=1; else dist[i][j]=0; } for(int i=1;i<=n;i++) cin>>str,mat[str]=i; cin>>m; while(m--) { cin>>str1>>w>>str2; dist[mat[str1]][mat[str2]]=w; //maap[mat[str1]][mat[str2]]=w; } Floyd(); printf("Case %d:",++cnt); bool flag=false; for(int i=1;i<=n;i++) if(dist[i][i]>1.0) { flag=true; break; } if(!flag) printf(" No\n"); else printf(" Yes\n"); } return 0; }
9、换货币,给出汇率和手续费,问是否可以使得手里钱增加 spfa
POJ 1860 Currency Exchange spfa
同样是换货币,之前的题是只有汇率,这个题还有手续费,spfa bellman都可做,我当时用的前者。为什么要用这两个方法,因为要判断"负环",当然了这个题存在“钱无限增加”的情况就说明松弛操作可以进行很多次,就是正常函数返回-1的情况==
#include <iostream> #include <cstdio> #include <cmath> #include <queue> #include <string.h> using namespace std; const int INF=0x3f3f3f3f; const int maxm=511111; const int maxn=111111; struct EdgeNode { int to; int next; double rate,comm,w; }; int cont[maxn]; EdgeNode edges[maxm]; int N,M,s; double val; int head[maxn],edge; bool vis[maxn]; queue <int> que; double dis[maxn]; void addedge(int u,int v,double r,double c) { edges[edge].comm=c,edges[edge].to=v; edges[edge].next=head[u];edges[edge].w=0; edges[edge].rate=r;head[u]=edge++; } void init() { memset(head,-1,sizeof(head)); edge=0; memset(cont,0,sizeof(cont)); memset(vis,false,sizeof(vis)); for (int i=0; i<N; i++) dis[i]=0; } int spfa(int s,int n)//单源最短路(s为起点,n为节点总数)/// { int u; while (!que.empty()) que.pop(); vis[s]=true; dis[s]=val; ++cont[s]; que.push(s); while (!que.empty()) { u=que.front(); que.pop(); vis[u]=false; for (int i=head[u]; i!=-1; i=edges[i].next) { int v=edges[i].to; edges[i].w=(dis[u]-edges[i].comm)*edges[i].rate-dis[u]; if (dis[v]<dis[u]+edges[i].w)/// { dis[v]=dis[u]+edges[i].w; if (!vis[v]) { vis[v]=true; que.push(v); } if(++cont[v]>n) return -1; } } } return 1; } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d%d%lf",&N,&M,&s,&val)) { int a,b; init(); double rab,cab,rba,cba; while(M--) { scanf("%d%d%lf%lf%lf%lf",&a,&b,&rab,&cab,&rba,&cba); addedge(a,b,rab,cab); addedge(b,a,rba,cba); } if(spfa(s,N)==-1)printf("YES\n"); else printf("NO\n"); } return 0; }
10、差分约束入门题 分糖果“小于等于” by the way
小于等于是最短路 大于等于是最长路
POJ 3159 Candies差分约束系统 spfa
核心在于:xj-xi<=bk,会发现它类似最短路中的三角不等式d[v]<=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=30005; int dis[maxn],head[maxn]; int n,m,sum; bool vis[maxn]; struct node{ int v,w,next; }edge[150010]; void addedge(int a,int b,int c){ edge[sum].v=b; edge[sum].w=c; edge[sum].next=head[a]; head[a]=sum++; } bool spfa(int s){ int stack[maxn],outque[maxn],top=0; memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); memset(outque,0,sizeof(outque)); stack[top++]=s; vis[s]=1;//vis数组的作用不是判断它是否被更新过 而是是否在栈里! dis[s]=0; while(top){ int tmp=stack[--top]; vis[tmp]=0; outque[tmp]++; if(outque[tmp]>n) return 0; //判断负环,当然这里没有必要写它 int k=head[tmp]; while(k>-1){ if(dis[edge[k].v]>edge[k].w+dis[tmp]){ dis[edge[k].v]=edge[k].w+dis[tmp]; if(vis[edge[k].v]==0){ vis[edge[k].v]=1; stack[top++]=edge[k].v; } } k=edge[k].next; } } return 1; } int main() { //freopen("cin.txt","r",stdin); while(cin>>n>>m){ sum=0; memset(head,-1,sizeof(head)); int a,b,c; while(m--){ scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); } spfa(1); printf("%d\n",dis[n]); } return 0; }
11、差分约束系统论文题
zoj1420Cashier Employment
题意:每天每个时间有一定的收银员人数需求(以小时为单位)另有很多人应聘,一旦应聘,干满8小时,问最少雇佣多少人
s[]数组表示共雇佣了的人数,num[] 当前时刻最多可雇佣人数,r[]某时刻的最少人数。1. s[i]-s[i-1]>=0 2. s[i-1]-s[i]>=-num[i] (1<=i<=24) 3. s[i]-s[i-8]>=r[i](8<=i<=24) 4. s[i]-s[i+16]>=r[i]-ans (1<=i<=8) 5. s[24]-s[0]>=ans
注意第4个式子,是将第3个式子扩展到"每天"得到的
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int inf=0x3f3f3f3f; const int maxn=50005; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[maxn]; void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } bool vis[maxn]; int cnt[maxn]; int dist[maxn]; bool spfa(int start,int n) { memset(vis,false,sizeof(vis)); memset(dist,-inf,sizeof(dist)); vis[start]=true; dist[start]=0; queue<int>que; while(!que.empty()) que.pop(); que.push(start); memset(cnt,0,sizeof(cnt)); cnt[start]=1; while(!que.empty()) { int u=que.front(); que.pop(); vis[u]=false; for(int i=0;i<E[u].size();i++) { int v=E[u][i].v; if(dist[v]<dist[u]+E[u][i].cost) { dist[v]=dist[u]+E[u][i].cost; if(!vis[v]) { vis[v]=true; que.push(v); if(++cnt[v]>n) return false; } } } } return true; } int num[30],r[30]; int main() { //freopen("cin.txt","r",stdin); int t,ans,m; scanf("%d",&t); while(t--) { for(int i=1;i<=24;i++) scanf("%d",&r[i]); for(int i=0;i<25;i++) num[i]=0; scanf("%d",&m); for(int i=0;i<m;i++) { int tmp; scanf("%d",&tmp); num[tmp+1]++;///!!! } ans=1; bool flag=true; int l=0,right=m; while(l<right) { for(int i=0;i<=24;i++) E[i].clear(); ans=(l+right)/2; for(int i=1;i<=24;i++) { addedge(i-1,i,0); addedge(i,i-1,-num[i]); } for(int i=8;i<=24;i++) addedge(i-8,i,r[i]); for(int i=1;i<=8;i++) addedge(i+16,i,r[i]-ans); addedge(0,24,ans); if(spfa(0,25)) right=ans,flag=false; else l=ans+1; } if(!flag) printf("%d\n",right); else printf("No Solution\n"); } return 0; }
12、差分约束系统水题
hdu1531King
题意:给出asi+asi+1+…+asi+n大于或者小于ki 那么设从a[1]到a[i]的和为一个数,把它转化成最长路或者最短路都可做
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int inf=0x3f3f3f3f; const int maxn=500; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[maxn]; void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } bool vis[maxn]; int cnt[maxn]; int dist[maxn]; bool spfa(int start,int n) { memset(vis,false,sizeof(vis)); memset(dist,-inf,sizeof(dist)); vis[start]=true; dist[start]=0; queue<int>que; while(!que.empty()) que.pop(); que.push(start); memset(cnt,0,sizeof(cnt)); cnt[start]=1; while(!que.empty()) { int u=que.front(); que.pop(); vis[u]=false; for(int i=0;i<E[u].size();i++) { int v=E[u][i].v; if(dist[v]<dist[u]+E[u][i].cost) { dist[v]=dist[u]+E[u][i].cost; if(!vis[v]) { vis[v]=true; que.push(v); if(++cnt[v]>n) return false; } } } } return true; } int main() { // freopen("cin.txt","r",stdin); int n,m,si,ni,ki; char oi[4]; while(~scanf("%d",&n)&&n) { scanf("%d",&m); for(int i=0;i<=n+2;i++) E[i].clear(); while(m--) { scanf("%d%d%s%d",&si,&ni,oi,&ki); if(oi[0]=='g') addedge(si-1,si+ni,ki+1); else addedge(si+ni,si-1,1-ki); } for(int i=0;i<=n;i++)addedge(n+1,i,0); if(spfa(n+1,n+2)) puts("lamentable kingdom"); else puts("successful conspiracy"); } return 0; }
13、差分约束系统较难题 :跳房子
hdu3440House Man【差分约束系统】
题意:由左至右一堆高矮不同的小房子,只能从左向右水平距离不超过d个房子,问水平最多走多远
做法:先为存有(点序号,房子高度)的结构体数组排序,枚举相邻高度的房子,为其加"d"的边,跑最短路。注意最终求的是最左边点(起点)和最右边点(终点)
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f; const int maxn=1005; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[maxn]; void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } bool vis[maxn]; int cnt[maxn]; int dist[maxn]; bool spfa(int start,int n) { memset(vis,false,sizeof(vis)); memset(dist,inf,sizeof(dist)); vis[start]=true; dist[start]=0; queue<int>que; while(!que.empty()) que.pop(); que.push(start); memset(cnt,0,sizeof(cnt)); cnt[start]=1; while(!que.empty()) { int u=que.front(); que.pop(); vis[u]=false; for(int i=0;i<E[u].size();i++) { int v=E[u][i].v; if(dist[v]>dist[u]+E[u][i].cost) { dist[v]=dist[u]+E[u][i].cost; if(!vis[v]) { vis[v]=true; que.push(v); if(++cnt[v]>n) return false; } } } } return true; } struct node { int val,pos; }nt[maxn]; bool cmp(node a,node b) { return a.val<b.val; } int main() { // freopen("cin.txt","r",stdin); int t,n,d,cas=1; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&d); for(int i=1;i<=n;i++) scanf("%d",&nt[i].val),nt[i].pos=i; sort(nt+1,nt+1+n,cmp); for(int i=0;i<=n;i++) E[i].clear(); for(int i=1;i<n;i++) { addedge(i+1,i,-1); if(nt[i].pos<nt[i+1].pos) addedge(nt[i].pos,nt[i+1].pos,d); else addedge(nt[i+1].pos,nt[i].pos,d); } printf("Case %d: ",cas++); int minn,maxn; if(nt[1].pos<nt[n].pos)minn=nt[1].pos,maxn=nt[n].pos; else minn=nt[n].pos,maxn=nt[1].pos; if(!spfa(minn,n)) printf("-1\n"); else printf("%d\n",dist[maxn]); } return 0; }
14、spfa最短路+tsp状压
Hdu 4568 Hunter【spfa最短路 tsp状态压缩】
题意:n*n的矩阵,每一个格子都有相应的花费,k个宝藏的位置。从任意边界进入任意边界出去。求出得到所有宝藏的最小花费。
思路:将边界作为0点。bfs求出宝藏之间以及宝藏与0点的最短距离。一次TSP,在图中跑一次回路,从起点0回到起点,得到最小花费。
附上的也是一个13级妹子的代码,比我的那份写的好看太多
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; int a[205][205], d[205][205]; int x[205], y[205], dp[1<<15][15]; bool v[205][205]; struct node { int x, y, c; node(int xx, int yy, int cc) { x = xx; y = yy; c = cc; } bool operator <(const node & A) const { return c > A.c; } }; int dx[]={1, -1, 0, 0}, dy[]={0, 0, 1, -1}; int n, m, k; bool ok(int x, int y) { if(x < 0 || y < 0 || x >= n || y >= m || a[x][y] == -1) { return false; } return true; } void bfs(int s, int t) { priority_queue<node> que; memset(v, 0, sizeof(v)); que.push(node(x[s], y[s], 0)); v[x[s]][y[s]] = 1; int c = 0; while(!que.empty()) { node e = que.top(); que.pop(); if(e.x == x[t] && e.y == y[t]) { c = e.c - a[x[t]][y[t]]; } for(int i = 0; i < 4; ++i) { int nx = e.x+dx[i], ny = e.y+dy[i]; if(v[nx][ny] || a[nx][ny] == -1) continue; if(nx < 0 || ny < 0 || nx >= n || ny >= m) { d[s][0] = min(d[s][0], e.c); d[0][s] = min(d[0][s], e.c); continue; } v[nx][ny] = 1; que.push(node(nx, ny, e.c+a[nx][ny])); } } if(s != t) { d[s][t] = c; d[t][s] = c; } } void deal_p_p() { memset(d, 0x3f, sizeof(d)); for(int i = 1; i <= k; ++i) { for(int j = 1; j <= k; ++j) { //while(!que.empty()) que.pop(); bfs(i, j); } } } int solve() { memset(dp, 0x3f, sizeof(dp)); int nk = k+1; dp[(1<<nk)-1][0] = 0; for(int s = (1<<nk)-2; s >= 0; --s) { for(int v = 0; v < nk; ++v) { for(int u = 0; u < nk; ++u) { if(!(s>>u &1)) { dp[s][v] = min(dp[s][v], dp[s|1<<u][u]+d[v][u]); } } } } return dp[0][0]; } int main() { //freopen("in", "r", stdin); int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); memset(a, 0, sizeof(a)); for(int i = 0; i < n; ++i) { for(int j = 0; j < m; ++j) { scanf("%d", &a[i][j]); } } int Sum = 0; scanf("%d", &k); memset(x, 0, sizeof(x)); memset(y, 0, sizeof(y)); for(int i = 1; i <= k; ++i) { scanf("%d%d", &x[i], &y[i]); Sum += a[x[i]][y[i]]; } deal_p_p(); if(k == 1) { if(d[1][0] == INF) { printf("0\n"); } else printf("%d\n", 2*d[1][0]+a[x[1]][y[1]]); } else { int ans = solve(); if(ans == INF) printf("0\n"); else printf("%d\n", ans+Sum); } } return 0; }
15、取消一点使得最短路有最大值
hdu5137How Many Maos Does the Guanxi Worth
题意:暴发户给孩子办关系升学,需要一个人找一个人,人情钱都是这货出,你要劝说其中一个人不去帮忙使得暴发户花钱最多
枚举所有点,逐次跑最短路即可
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=33; const int inf=0x3f3f3f3f; bool vis[maxn]; int pre[maxn],lowcost[maxn],cost[maxn][maxn]; void dijkstra(int n,int beg) { for(int i=0;i<n;i++) { lowcost[i]=inf;vis[i]=false;pre[i]=-1; } lowcost[beg]=0; for(int j=0;j<n;j++) { int k=-1; int Min=inf; for(int i=0;i<n;i++) if(!vis[i]&&lowcost[i]<Min) { Min=lowcost[i]; k=i; } if(k==-1)break; vis[k]=true; for(int i=0;i<n;i++) if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i]) { lowcost[i]=lowcost[k]+cost[k][i]; pre[i]=k; } } } int temp[maxn]; int main() { // freopen("cin.txt","r",stdin); int n,m; while(~scanf("%d%d",&n,&m)&&n&&m) { memset(cost,inf,sizeof(cost)); for(int i=0;i<m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); a--;b--; if(c>cost[a][b]) continue; cost[a][b]=cost[b][a]=c; } int ans=0; for(int i=1;i<n-1;i++) { for(int j=0;j<=n-1;j++) { temp[j]=cost[i][j]; cost[i][j]=cost[j][i]=inf; } dijkstra(n,0); if(lowcost[n-1]>ans) ans=lowcost[n-1]; for(int j=0;j<n;j++) cost[i][j]=cost[j][i]=temp[j]; } if(ans<inf) printf("%d\n",ans); else printf("Inf\n"); } return 0; }
16、最短路反向建图(酋长是终点不是起点,最开始所有点前面再加一个源点)枚举等级区间依次跑最短路即可
poj1062昂贵的聘礼
#include <iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=133; const int inf=0x3f3f3f3f; bool vis[maxn]; int pre[maxn],lowcost[maxn],cost[maxn][maxn],val[maxn],rank[maxn]; void dijkstra(int cost[][maxn],int n,int beg) { for(int i=0;i<n;i++) { lowcost[i]=inf;vis[i]=false;pre[i]=-1; } lowcost[beg]=0; for(int j=0;j<n;j++) { int k=-1; int Min=inf; for(int i=0;i<n;i++) if(!vis[i]&&lowcost[i]<Min) { Min=lowcost[i]; k=i; } if(k==-1)break; vis[k]=true; for(int i=0;i<n;i++) if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i]) { lowcost[i]=lowcost[k]+cost[k][i]; pre[i]=k; } } } int tmp[maxn][maxn]; int main() { freopen("cin.txt","r",stdin); int m,n; while(~scanf("%d%d",&m,&n)) { memset(cost,inf,sizeof(cost)); int maxn=0,minn=inf; for(int i=0;i<n;i++) { int x; scanf("%d%d%d",&cost[n][i],&rank[i],&x); if(rank[i]>maxn)maxn=rank[i]; if(rank[i]<minn)minn=rank[i]; while(x--) { int t,v; scanf("%d%d",&t,&v);t--; if(v<cost[i][t]) cost[i][t]=v; } } int ans=inf; for(int k=max(0,rank[0]-m);k<=rank[0]+m;k++) { for(int i=0;i<n;i++) for(int j=0;j<n;j++) { if(rank[i]>=k&&rank[i]<=k+m&&rank[j]>=k&&rank[j]<=k+m) tmp[i][j]=cost[i][j]; else tmp[i][j]=inf; } dijkstra(tmp,n,0); for(int i=0;i<n;i++) if(ans>lowcost[i]+cost[n][i]) ans=lowcost[i]+cost[n][i]; } printf("%d\n",ans); } return 0; }
17、送餐来回的最短距离
看到这个题的时候想到上面的第7题了,然而第7题不需要每个点都遍历一遍,而这个题需要,所以这个是需要用状压的tsp的
poj3311 Hie with the Pie【floyd最短路+状态压缩】
#include <iostream> #include<cstdio> #include<cstring> using namespace std; int dp[1<<11][20],n,num[11][11]; int min(int a,int b){if(a<b)return a;return b;} int judge(int s) { int num=0; while(s) { if(s&1)num++; s>>=1; } return num; } int dist[11][11]; void init() { for(int i=0;i<n;i++) for(int j=0;j<n;j++) { dist[i][j]=num[i][j]; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) if(dist[j][i]+dist[i][k]<dist[j][k]) dist[j][k]=dist[j][i]+dist[i][k]; for(int i=0;i<n;i++) for(int j=0;j<n;j++); // printf("i=%d,j=%d,dist=%d\n",i,j,dist[i][j]); } int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d",&n)&&n) { for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) scanf("%d",&num[i][j]); memset(dp,0x3f3f3f3f,sizeof(dp)); dp[0][0]=0; n++;//have n+1 points init(); for(int i=0;i<n;i++)//n points dp[1<<i|1][i]=dist[0][i]; // for(int i=0;i<(1<<n)-1;i++) if(dp[i]!=0x3f3f3f3f)printf("i=%d dp=%d ",i,dp[i]); int ans=0x3f3f3f3f; for(int i=1;i<(1<<n)-1;i++)//now { for(int j=0;j<n;j++)//not exist { if(i&(1<<j)) continue; for(int k=0;k<n;k++)//exist { if(j==k) continue; if(i&(1<<k)) { dp[1<<j|i][j]=min(dp[i][k]+dist[k][j],dp[1<<j|i][j]); if(judge(1<<j|i)==n) ans=min(ans,dp[1<<j|i][j]+dist[j][0]); } } } } printf("%d\n",ans); } return 0; }
18、建全国的路的一部分,依旧要求联通,但是首都到各个城市的最短距离不变的前提下求最小花费
Aizu 2249Road Construction 单源最短路变形 spfa模板改写
既然是首都到各地的距离最短前提下求最小花费,那么松弛操作当距离相等的时候压入花费小的就可以啦
#include <stdio.h> #include <string.h> #include <queue> #include <iostream> #include <algorithm> using namespace std; const int maxn=10050; const int maxe=2005000; const int INF=1e9; struct note { int to; int w; int c; int next; }; note edge[maxe]; int head[maxn]; int ip; void init() { memset(head,-1,sizeof(head)); ip=0; } void addedge(int u,int v,int w,int c) { edge[ip].to=v; edge[ip].c=c; edge[ip].w=w; edge[ip].next=head[u]; head[u]=ip++; } int cost[maxn]; int dis[maxn]; bool vis[maxn]; queue<int>q; void spfa(int s,int n) { while(!q.empty())q.pop(); for(int i=1;i<=n;i++) cost[i]=dis[i]=INF; memset(vis,false,sizeof(vis)); dis[s]=0; cost[s]=0; vis[s]=true; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i!=-1;i=edge[i].next) { int to=edge[i].to; int val=edge[i].w; if(dis[to]>dis[u]+val||(dis[to]==dis[u]+val&&cost[to]>edge[i].c))//有多条最短路时,取花费最小的 { dis[to]=dis[u]+val; cost[to]=edge[i].c; if(!vis[to]) { q.push(to); vis[to]=true; } } } } } int main() { // freopen("cin.txt","r",stdin); int n,m; int u,v,w,c; while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; init(); for(int i=0;i<m;i++) { scanf("%d%d%d%d",&u,&v,&c,&w); addedge(u,v,c,w); addedge(v,u,c,w); } spfa(1,n); int ans=0; for(int i=1;i<=n;i++) ans+=cost[i]; printf("%d\n",ans); } return 0; }
19、差分约束系统
uva11478 Halum【二分+差分约束】
题意:对于给定有向图,定义一种操作:对于选定的某一点,进入这个点的所有边权都减去d,从这个点出去的所有边权都加上d。经过一系列的操作,使得所有边权的最小值达到最大,需保证这个最值是正数
把点当做最短路的点假设每个点都有一个点权sum(a),而w(a,b)+sum(a)-sum(b)>=x,x是所有边权的最小值,把边权sum放到等号右边,就构成了差分约束的不等式组,,保证不出现负环的spfa就是最大的x求得的。
by the way Spfa要是最开始不加超级源点,就要把所有的点都压入队列
这么看来就和上面的题一样啦
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int inf=0x3f3f; const int maxn=505; int n,m; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[maxn]; void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } bool vis[maxn]; int cnt[maxn]; int dist[maxn]; bool spfa(int start,int n) { memset(vis,false,sizeof(vis)); memset(dist,inf,sizeof(dist)); vis[start]=true; dist[start]=0; queue<int>que; while(!que.empty()) que.pop(); que.push(start); memset(cnt,0,sizeof(cnt)); cnt[start]=1; while(!que.empty()) { int u=que.front(); que.pop(); vis[u]=false; for(int i=0;i<E[u].size();i++) { int v=E[u][i].v; if(dist[v]>dist[u]+E[u][i].cost) { dist[v]=dist[u]+E[u][i].cost; if(!vis[v]) { vis[v]=true; que.push(v); if(++cnt[v]>n) return false; } } } } return true; } bool judge(int x) { // cout<<"rrr"<<endl; for(int i=1;i<=n;i++) { for(int j=0;j<E[i].size();j++) E[i][j].cost-=x; } bool flag=spfa(0,n); for(int i=1;i<=n;i++) { for(int j=0;j<E[i].size();j++) E[i][j].cost+=x; } return flag; } int main() { //freopen("cin.txt","r",stdin); // int cas=1; // scanf("%d",&t); while(~scanf("%d%d",&n,&m)) { // printf("Case #%d: ",cas++); for(int i=0;i<=n;i++) E[i].clear(); // memset(head,-1,sizeof(head)); int l=1,mid,r=-inf; while(m--) { int a,b; int c; scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); if(c>r)r=c; } for(int i=1;i<=n;i++)addedge(0,i,0); // cout<<"rrr"<<endl; // printf("r=%d\n",r); if(judge(r)) { printf("Infinite\n"); continue; } else if(!judge(1)) { puts("No Solution"); continue; } int ans; while(l<=r) { mid=(l+r)/2; // cout<<mid<<endl; // printf("l=%d,mid=%d,r=%d ",l,mid,r); if(judge(mid)) ans=mid,l=mid+1; else {r=mid-1;} // printf("l=%d,mid=%d,r=%d,ans=%d\n",l,mid,r,ans); } printf("%d\n",ans); } return 0; }
20、二分找负环
UVA - 11090 Going in Cycle!! 【Bellman-Ford算法判负环 二分】
一样的题:给定有向带权图,求最小环的值。二分找最小值,边权都减去这个数
#include <iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; const double inf=0x3f3f3f3f; const int maxn=100; int n,m,t; double dist[maxn]; struct Edge { int u,v; double cost; Edge(int _u=0,int _v=0,double _cost=0):u(_u),v(_v),cost(_cost){} }; vector<Edge>E; bool bellman(int start,int n) { for(int i=1;i<=n;i++)dist[i]=inf; dist[start]=0; for(int i=1;i<n;i++) { bool flag=false; for(int j=0;j<E.size();j++) { int u=E[j].u; int v=E[j].v; double cost=E[j].cost; if(dist[v]>dist[u]+cost) { dist[v]=dist[u]+cost; flag=true; } } if(!flag)return true; } for(int j=0;j<E.size();j++) if(dist[E[j].v]>dist[E[j].u]+E[j].cost) return false; return true; } bool judge(double x) { for(int i=0;i<E.size();i++) E[i].cost-=x; bool flag=bellman(1,n); for(int i=0;i<E.size();i++) E[i].cost+=x; return flag; } int main() { //freopen("cin.txt","r",stdin); int cas=1; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); printf("Case #%d: ",cas++); E.clear(); double r=0.0; while(m--) { int a,b; double c; scanf("%d%d%lf",&a,&b,&c); E.push_back(Edge(a,b,c)); if(c>r)r=c; } // printf("r=%lf\n",r); if(judge(r+1)) { printf("No cycle found.\n"); continue; } double l=0.0,mid; while(r-l>=0.0001) { mid=(l+r)/2; for(int i=0;i<E.size();i++) E[i].cost-=mid; if(bellman(1,n)) l=mid; else r=mid; for(int i=0;i<E.size();i++) E[i].cost+=mid; } printf("%.2lf\n",r); } return 0; }
21、
hdu4786Fibonacci Tree【kruskal最小生成树】
给定无向图每个边要么是白色,要么是黑色。求生成树的白边个数能否是斐波那契数。其实应该能想到的,让白边是1,黑边是0,求最大生成树和最小生成树。看中间范围是否有斐波那契数。
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=200005; const int maxm=200005; int F[maxn]; struct Edge { int u,v,w; }edge[maxm]; int tol; void addedge(int u,int v,int w) { edge[tol].v=v; edge[tol].u=u; edge[tol++].w=w; } bool cmp(Edge a,Edge b) { return a.w<b.w; } bool cmp2(Edge a,Edge b) { return a.w>b.w; } int fnd(int x) { return x==F[x]?x:F[x]=fnd(F[x]); } int kruskal(int n) { for(int i=1;i<=n;i++) F[i]=i; // sort(edge,edge+tol,cmp); int cnt=0; int ans=0; for(int i=0;i<tol;i++) { int u=edge[i].u; int v=edge[i].v; int w=edge[i].w; int a=fnd(u); int b=fnd(v); if(a!=b) { ans+=w; F[b]=a; cnt++; } if(cnt==n-1)break; } if(cnt<n-1)return -1; return ans; } int num[100],cnt; void init() { num[0]=0;num[1]=1;num[2]=2;num[3]=3; cnt=4; while(num[cnt-1]<=100005) { num[cnt]=num[cnt-1]+num[cnt-2]; cnt++; // cout<<num[cnt-1]<<endl; } } int main() { //freopen("cin.txt","r",stdin); int t,n,m,cas=1; scanf("%d",&t); init(); while(t--) { scanf("%d%d",&n,&m); tol=0; while(m--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w);///!!! } printf("Case #%d: ",cas++); sort(edge,edge+tol,cmp); int small=kruskal(n); bool ff=true; sort(edge,edge+tol,cmp2); int big=kruskal(n); // printf("s=%d,b=%d\n",small,big); if(small==-1||big==-1) { puts("No"); continue; } bool flag=false; for(int i=1;i<=23;i++) { if(num[i]>=small&&num[i]<=big) { flag=true; break; } } if(flag) printf("Yes\n"); else printf("No\n"); } return 0; }
22、2015多校第一场(盗墓笔记)
hdu5294Tricks Device【最短路+网络流】
从1开始到n结束,给出双向道路,问至少去掉多少边最短路不是原始最短路长度,至多去掉多少条边最短路长度不变
分别利用1与n作为源点 ,分别跑一遍最短路,这个时候两个dist数组记录的就是以1、n为源点的单源最短路长度,理论上他俩无缝接起来都是最短路的方案(理论上是,实际上也是:P )那么我就依次验证给定的边长是否在最短路上,在的话,给这对点建网络流的边(这个题最最讨厌的地方在于函数名,数组名都差不多,特别容易写错)流量是题中给定的第三个数组,求完最短路还要给这对点再建一遍最短路的边,边长为一,这样跑下来dist[n]就是最短路的最短长度了
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int inf=0x3f3f; const int maxn=2111; int n,m; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[maxn]; void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } bool vis[maxn]; int cnt[maxn]; int dist[maxn],dist1[maxn]; bool spfa(int start,int n) { memset(vis,false,sizeof(vis)); memset(dist,inf,sizeof(dist)); vis[start]=true; dist[start]=0; queue<int>que; while(!que.empty()) que.pop(); que.push(start); memset(cnt,0,sizeof(cnt)); cnt[start]=1; while(!que.empty()) { int u=que.front(); que.pop(); vis[u]=false; for(int i=0;i<E[u].size();i++) { int v=E[u][i].v; if(dist[v]>dist[u]+E[u][i].cost) { dist[v]=dist[u]+E[u][i].cost; if(!vis[v]) { vis[v]=true; que.push(v); if(++cnt[v]>n) return false; } } } } return true; } int w[60009],u[60009],v[60009]; //////////////////////////////////////// const int mm=161111; const int mn=600009; const int oo=1000000000; int node,src,dest,edge; int reach[mm],flow[mm],nxt[mm]; int head[mn],work[mn],dis[mn],q[mn]; inline int min(int a,int b) { return a<b?a:b; } inline void prepare(int _node,int _src,int _dest) { node=_node,src=_src,dest=_dest; for(int i=0;i<node;++i)head[i]=-1; edge=0; } inline void addedge(int u,int v,int c1,int c2) { reach[edge]=v,flow[edge]=c1,nxt[edge]=head[u],head[u]=edge++; reach[edge]=u,flow[edge]=c2,nxt[edge]=head[v],head[v]=edge++; } bool Dinic_bfs() { int i,u,v,l,r=0; for(i=0;i<node;++i)dis[i]=-1; dis[q[r++]=src]=0; for(l=0;l<r;++l) for(i=head[u=q[l]];i>=0;i=nxt[i]) if(flow[i]&&dis[v=reach[i]]<0) { dis[q[r++]=v]=dis[u]+1; if(v==dest)return 1; } return 0; } int Dinic_dfs(int u,int exp) { if(u==dest)return exp; for(int &i=work[u],v,tmp;i>=0;i=nxt[i]) if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0) { flow[i]-=tmp; flow[i^1]+=tmp; return tmp; }dis[u]--; return 0; } int Dinic_flow() { int i,ret=0,delta; while(Dinic_bfs()) { for(i=0;i<node;++i)work[i]=head[i]; while(delta=Dinic_dfs(src,oo))ret+=delta; } return ret; } int bb[mn][2],dist2[mn]; int main() { // freopen("cin.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { for(int i=0;i<=n;i++) E[i].clear(); for(int i=0;i<m;i++) { scanf("%d%d%d",&u[i],&v[i],&w[i]); addedge(u[i],v[i],w[i]); addedge(v[i],u[i],w[i]); } spfa(1,n); memcpy(dist2,dist,sizeof(dist)); spfa(n,n); int k=0; for(int i=0;i<m;i++) { if(dist2[u[i]]>dist2[v[i]])swap(u[i],v[i]); if(dist[v[i]]+w[i]+dist2[u[i]]==dist2[n]) { bb[k][0]=u[i]; bb[k++][1]=v[i]; } } prepare(n+1,1,n); for(int i=0;i<k;i++) { addedge(bb[i][0],bb[i][1],1,0); // addedge(bb[i][1],bb[i][0],1); } int x=Dinic_flow(); for(int i=0;i<=n;i++) E[i].clear(); for(int i=0;i<k;i++) { addedge(bb[i][0],bb[i][1],1); addedge(bb[i][1],bb[i][0],1); } spfa(1,n); printf("%d %d\n",x,m-dist[n]); } return 0; }
23、次小生成树(秦始皇)
hdu4081Qin Shi Huang‘s National Road System【次小生成树】
说题意:秦始皇统一中国之后要在全国修公路连接各个城市,抠门皇帝只想修成最小生成树(距离最小,不考虑人力),一个道士说自己可以不花人力物力修一条路,经过两方妥协,选择max(两个城市人口/生成树长度-这条路的长度)的路让他变,求这个比值最大值。
这个题没问次小生成树的值,但是用到了路径最长边这个数组,我叫他Max[][],题中问的是(两个城市人口/生成树长度-这条路的长度),我们首先考虑分母最小,因为对于某个边来说分子是定值啊,分母分成两种情况:1.这个边是最小生成树上的,那啥也别说,mst-cost[][]即可 2.这条边不在最小生成树上:那就是含有这个边的某个生成树,(这么说来这个题应该归纳到次小生成树啊==),mst-Max+cost即可
#include <iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int maxn=1010; const int inf=0x3f3f3f3f; bool vis[maxn],used[maxn][maxn]; double lowc[maxn]; int pre[maxn]; double Max[maxn][maxn],cost[maxn][maxn]; struct node { int x,y,pop; }num[1010]; double min(double a,double b){if(a<b) return a;return b;} double max(double a,double b){if(a>b) return a;return b;} inline double Dist(node v1,node v2) { return sqrt(double(v1.x-v2.x)*(v1.x-v2.x)+double(v1.y-v2.y)*(v1.y-v2.y)); } double prim(int n) { double ans=0; memset(vis,false,sizeof(vis)); memset(Max,0,sizeof(Max)); memset(used,false,sizeof(used)); vis[0]=true; pre[0]=-1; for(int i=0;i<n;i++) { lowc[i]=cost[0][i]; pre[i]=0; } // lowc[0]=0.0; for(int i=1;i<n;i++) { double minc=1.0*inf; int p=-1; for(int j=0;j<n;j++) if(!vis[j]&&(minc>lowc[j]||p==-1)) { minc=lowc[j]; p=j; } ans+=minc; vis[p]=true; used[p][pre[p]]=used[pre[p]][p]=true; for(int j=0;j<n;j++) { if(vis[j]&&j!=p)//j!=p必须加!!1 Max[j][p]=Max[p][j]=max(Max[j][pre[p]],lowc[p]); if(!vis[j]&&lowc[j]>cost[p][j]) { lowc[j]=cost[p][j]; pre[j]=p; } } } return ans; } int main() { //freopen("cin.txt","r",stdin); int t,n; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d%d%d",&num[i].x,&num[i].y,&num[i].pop); for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) cost[i][j]=cost[j][i]=Dist(num[i],num[j]); double mst=prim(n); // double mst=smst(n,ans); double ans=-1; for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) { if(used[i][j])//on mst ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-cost[i][j])); else ans=max(ans,1.0*(num[i].pop+num[j].pop)/(mst-Max[i][j])); } printf("%.2f\n",ans); } return 0; }