【Kruskal+dfs】BZOJ1016- [JSOI2008]最小生成树计数
2016-07-23 21:15
513 查看
【题目大意】
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。
【思路】
拖欠了三个月整(?)的题目,搞出来弄掉了……本年度写的时候姿势最丑的程序,完全不知道自己在搞些什么,晕乎乎的,算了。
首先,MST具有以下性质:
1.对于同一张无向加权图G,它的最小生成树中长度为L的边长度一定。
2.MST用Kruskal做,处理完长度<=x,此时图的连通性是确定的。
我其实没有读懂第二句话是什么意思,总之大概理解一下,然后乱搞!怎么搞呢。
先按照普通的Kruskal,按照边长排序,然后排序,然后记录下排序为i的长度有几条边numss,下标为nstart到nend。然后弄出一组MST的解。这个不需要单独搞,只需要在记录边的条数的时候一边操作一边进行Kruskal(详细见程序)。
注意一下做完之后有可能进行合并操作的次数,也就是选的边是小于N-1的,也就是没有生成树,特判一下。
接着dfs,枚举每条边取numss个。由于题目条件相同长度的边至多10个,只需暴搜索2^10。每次就判断一下当前这条边左右两边是否已经在同一个并查集里面了,如果不在就可以尝试取这条边合并两段,dfs;或者这条边不取,直接dfs下去。
嗯,然后乘法原理就好了!
什么乱七八糟的题解……
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。
【思路】
拖欠了三个月整(?)的题目,搞出来弄掉了……本年度写的时候姿势最丑的程序,完全不知道自己在搞些什么,晕乎乎的,算了。
首先,MST具有以下性质:
1.对于同一张无向加权图G,它的最小生成树中长度为L的边长度一定。
2.MST用Kruskal做,处理完长度<=x,此时图的连通性是确定的。
我其实没有读懂第二句话是什么意思,总之大概理解一下,然后乱搞!怎么搞呢。
先按照普通的Kruskal,按照边长排序,然后排序,然后记录下排序为i的长度有几条边numss,下标为nstart到nend。然后弄出一组MST的解。这个不需要单独搞,只需要在记录边的条数的时候一边操作一边进行Kruskal(详细见程序)。
注意一下做完之后有可能进行合并操作的次数,也就是选的边是小于N-1的,也就是没有生成树,特判一下。
接着dfs,枚举每条边取numss个。由于题目条件相同长度的边至多10个,只需暴搜索2^10。每次就判断一下当前这条边左右两边是否已经在同一个并查集里面了,如果不在就可以尝试取这条边合并两段,dfs;或者这条边不取,直接dfs下去。
嗯,然后乘法原理就好了!
什么乱七八糟的题解……
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define mod 31011 using namespace std; const int MAXN=1000+50; struct node { int fr,to,len; bool operator < (const node &x) const { return len<x.len; } }edge[MAXN]; int num[MAXN],numss=-1,nstart[MAXN],nend[MAXN];//num[i]长度排序为i的边长需要取多少个,nstart/nend表示长度排序为i的边长排序为几到几 int n,m,pa[MAXN],tot=0,ans,tmpans; int find(int x){return (pa[x]==x?x:find(pa[x]));} void dfs(int now,int r,int total,int num) { if (num>total) return; if (now>r) { if (num==total) tmpans++; return; } int u=edge[now].fr,v=edge[now].to; int fa=find(u),fb=find(v); if (fa!=fb) { pa[fa]=fb; dfs(now+1,r,total,num+1); pa[fa]=fa; } dfs(now+1,r,total,num); } void init() { scanf("%d%d",&n,&m); for (int i=0;i<m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); edge[i]=(node){a,b,c}; } sort(edge,edge+m); } void kruskal() { memset(num,0,sizeof(num)); for (int i=1;i<=n;i++) pa[i]=i; for (int i=0;i<m;i++) { if (i==0 || edge[i].len!=edge[i-1].len) { if (i!=1) nend[numss]=i-1; nstart[++numss]=i; } int fa=find(edge[i].fr),fb=find(edge[i].to); if (fa!=fb) { pa[fa]=fb; num[numss]++; tot++; } } nend[numss]=m-1; } void solve() { if (tot<n-1) puts("0"); else { ans=1; for (int i=1;i<=n;i++) pa[i]=i; for (int i=0;i<=numss;i++) { tmpans=0; dfs(nstart[i],nend[i],num[i],0); ans=(ans*tmpans)%mod; for (int j=nstart[i];j<=nend[i];j++) { int u=edge[j].fr,v=edge[j].to; int fa=find(u),fb=find(v); if (fa!=fb) pa[fa]=fb; } } printf("%d",ans); } } int main() { freopen("bzoj_1016.in","r",stdin); freopen("bzoj_1016.out","w",stdout); init(); kruskal(); solve(); return 0; }
相关文章推荐
- jstack和dump内存分析
- JS中的全局对象
- 编写《编辑器》阶段小结
- JavaScript高级程序设计(第3版)中文 高清 完整
- Javascript加载执行中的问题
- 欢迎使用CSDN-markdown编辑器
- 【bzoj1561】[JSOI2009]去括号
- javascript基础
- javascript继承详解(一):前世今生
- JS 用字符串生成动态变量名 或 字符串与已有变量对应
- debian pjsip 编译
- js桥
- 面向对于javascript编程
- 摘自:javascript的理解DOM事件流的三个阶段
- JavaScript 表单脚本
- javaScript实现简单的表单验证
- js 跨域详解
- javascript命名规范总结
- JavaScript struct类型与valueOf
- 12个非常实用的JavaScript小技巧