啦啦啦
又来喽
今天一直在做小生成树问题。总共刷了7题,用来两种算法,prime和kurskal。
而且还都是HDU畅通工程系列的。
上午把prime算法的模板谢了出来,然后用模板刷了三题。
HDU1863、HDU1233、HDU1879
由于代码神似呀,这里只贴1863的代码了。
代码:
#include<iostream> #include<cstring> #include<vector> using namespace std; const int maxn=101; //顶点数 const int INF=0x7fffffff; struct edge //边 { int to; //到达的点 int cost; //边的花费 bool flag; //是否入选 }; int choosed[maxn]; //已选边 int unchoosed[maxn]; //未选边 int nodeNum,edgeNum,MST; //顶点数、边数、最小生成树 bool choose[maxn]; //顶点是否已选 vector<edge> myV[maxn]; //图的邻接表 /* //这是无向图有重复边的建图,取重复边中最小的边存储 bool exist(int from,int to,int cost) { bool existFlag=false,smallFlag=false; for(int i=0;i<myV[from].size();i++) { if(myV[from][i].to==to) { existFlag=true; if(myV[from][i].cost>cost) { smallFlag=true; myV[from][i].cost=cost; break; } } } if(smallFlag) { for(int j=0;j<myV[to].size();j++) { if(myV[to][j].to==from) { myV[to][j].cost=cost; break; } } } if(existFlag) return true; return false; } void storeMap() //邻接表存图 { for(int j=0;j<maxn;j++) //清空 { myV[j].clear(); } memset(choose,false,sizeof(choose)); //标志图的各个点是否被选,不能重复 int from,to,cost,num=0; //从from到to花费cost的边 for(int i=0;i<edgeNum;i++) { scanf("%d%d%d",&from,&to,&cost); //把图上的所有点不重复的放到unchoosed[]表中 if(!choose[from]) { unchoosed[num++]=from; choose[from]=true; } if(!choose[to]) { unchoosed[num++]=to; choose[to]=true; } if(!exist(from,to,cost)) //图中存在重复边的处理 { edge tmp; tmp.flag=false; tmp.cost=cost; //无向图 tmp.to=to; myV[from].push_back(tmp); tmp.to=from; myV[to].push_back(tmp); } } } */ //无向图,无重复边的建图 void storeMap() //邻接表存图 { for(int j=0;j<maxn;j++) //清空 { myV[j].clear(); } memset(choose,false,sizeof(choose)); //标志图的各个点是否被选,不能重复 int from,to,cost,num=0; //从from到to花费cost的边 for(int i=0;i<edgeNum;i++) { scanf("%d%d%d",&from,&to,&cost); //把图上的所有点不重复的放到unchoosed[]表中 if(!choose[from]) { unchoosed[num++]=from; choose[from]=true; } if(!choose[to]) { unchoosed[num++]=to; choose[to]=true; } edge tmp; tmp.flag=false; tmp.cost=cost; //无向图 tmp.to=to; myV[from].push_back(tmp); tmp.to=from; myV[to].push_back(tmp); } } void prime() //prime算法 { //初始化一些信息 MST=0; memset(choose,false,sizeof(choose)); choosed[0]=unchoosed[0]; choose[unchoosed[0]]=true; int choosedNum=1,from,to,cost,index; bool flag; while(choosedNum<nodeNum) { cost=INF; flag=false; for(int i=0;i<choosedNum;i++) { for(int j=0;j<myV[choosed[i]].size();j++) { edge tmp=myV[choosed[i]][j]; if(!choose[tmp.to] && !tmp.flag && tmp.cost<cost) { flag=true; //表示该次找到了一次路径 from=choosed[i]; to=tmp.to; cost=tmp.cost; index=j; } } } if(!flag) break; //当顶点没有都被选择时,只有有一次没能找到能够加入的顶点,图不畅通 myV[from][index].flag=true; for(int k=0;k<myV[to].size();k++) { if(myV[to][k].to==from) { myV[to][k].flag=true; break; } } choosed[choosedNum++]=to; choose[to]=true; MST+=cost; } if(flag) printf("%d\n",MST); else printf("?\n"); } int main() { while(scanf("%d%d",&edgeNum,&nodeNum)==2,edgeNum) //输入图的点数、边数 { storeMap(); prime(); } system("pause"); return 0; }
可是下午第一题(HDU1875)就不顺喽,TLE、TLE、TLE。。。
prime算法O(n^2)的时间复杂度。。。
后来没办法,有写了kruskal算法模板,A啦
代码:
#include<iostream> #include<cmath> #include<queue> using namespace std; const int maxn=101; //图的最大节点数 struct edge //边 { int from; //起点 int to; //终点 double cost; //路径长 friend bool operator < (const edge &e1,const edge &e2) { return e1.cost>e2.cost; } }; struct location //小岛位置 { int x,y; }temp[maxn]; int father[maxn]; //用来做并查集 int nodeNum,edgeNum; //顶点数、边数 double MST; //最小生成树 priority_queue<edge> myQ; //kruskal算法,存放边 void inputLocation() //输入小岛位置 { for(int i=0;i<nodeNum;i++) { scanf("%d%d",&temp[i].x,&temp[i].y); } } void storeMap() //存储岛的桥构成的图 { while(!myQ.empty()) myQ.pop(); //清空 double dis,cost; edgeNum=0; for(int from=0;from<nodeNum;from++) { for(int to=from+1;to<nodeNum;to++) { dis=sqrt(pow(double(temp[from].x-temp[to].x),2)+pow(double(temp[from].y-temp[to].y),2)); if(dis>=10 && dis<=1000) { cost=dis*100; } else { continue; //如果两岛间距离不满足条件,该边不存在 } edgeNum++; edge e; e.from=from; e.to=to; e.cost=cost; myQ.push(e); } } } int find(int x) //查找 { if(x==father[x]) return father[x]; return father[x]=find(father[x]); } bool judge() //判断是否是一棵最小生成树 { int f=find(0); for(int i=1;i<nodeNum;i++) { if(f!=find(i)) { return false; } } return true; } void kruskal() //kruskal算法 { MST=0; for(int i=0;i<maxn;i++) { father[i]=i; } int num=0; //记录MST的边数 while(!myQ.empty() && num!=nodeNum-1) { edge e=myQ.top(); myQ.pop(); int fx=find(e.from); int fy=find(e.to); if(fx!=fy) { father[fx]=fy; MST+=e.cost; num++; } } if(judge()) printf("%.1lf\n",MST); else printf("oh!\n"); } int main() { int cas; scanf("%d",&cas); while(cas--) { scanf("%d",&nodeNum); inputLocation(); storeMap(); if(edgeNum<nodeNum-1) //特殊情况的处理 { printf("oh!\n"); continue; } kruskal(); } system("pause"); return 0; }
然后呢,又用kruskal模板A了HDU1301、1162、1102
基本就没遇到啥障碍了,求最小生成树没太大的问题,有时需要在建图上做点文章,其他都很套路化。
还有就是kruskal算法的时间复杂度为O(nlogn)的。
另外,还想说的是STL给我们带来很大的方便,包含头文件,直接调函数,个人觉得还是不能过度依赖。。。
最后,明天下午是第三次比赛啦。
听说不会太难的,希望自己能AK啦。。。