传送门:http://codeforces.com/problemset/problem/445/B
参考:https://blog.csdn.net/littlewhite520/article/details/77018559
题意:
有N种药剂编号 1 ~ N,然后有M种反应关系,这里有一个试管,开始时危险系数为 1,每当放入的药剂和瓶子里面的药剂发生反应时危险系数会乘以2,(注意,不管会发生反映的有几组,只要在同一次加入的,只乘一个2;)否则就不变,给出N个药剂和M种反应关系,求最大的危险系数。
思路:这个思路是真的优秀,感觉是一种逆向思维:
我们假设 1 ~ N 有 M 种反应关系 ,如果有反应关系的我们可以把他们看成是一个集合 ,假设这M种反应构成了 T个集合,那么把这T个集合中的元素依次放入试管,有几个不发生反应呢?当然是T个了,把每个集合看成是一课树,根节点和其子节点反应,这个子节点又和它的子节点发生反应、想一想是一个链状的结构、、、假设每个集合放入时先放根节点(第一个节点),那么每个根节点是不是都不和当前试管中的药剂发生反应呢,因为根节点只和它的子节点发生发应,而他的子节点尚未放入、、所以把这些药剂全部放入,最少只需要 T 个不发生发应。
至于找集合的个数,可以用并查集或者dfs;
dfs的:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define pb push_back typedef long long ll; using namespace std; const int maxn = 55; vector<int>mp[maxn]; int vis[maxn]; void dfs(int v) { vis[v] = 1; for(int i=0;i<mp[v].size();i++) { int tmp = mp[v][i]; if(vis[tmp]==0) { dfs(tmp); } } return ; } int main(){ int n,m; scanf("%d%d",&n,&m); memset(vis,0,sizeof(vis)); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); mp[a].pb(b); mp[b].pb(a); } ll ans = 1; int t = 0; for(int i=1;i<=n;i++) { if(vis[i]==0) { dfs(i); t++; } } t = n - t; for(int i=1;i<=t;i++) { ans*=2; } printf("%lld\n",ans); return 0; }
并查集:
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 55; int n,m; int fa[maxn]; void init(){ for(int i=1;i<=n;i++) fa[i]=i; } int finx(int x) { if(fa[x]==x)return x; else return fa[x] = finx(fa[x]); } void uni(int x,int y) { int px = finx(x); int py = finx(y); if(px==py)return ; else fa[px] = py; } int main(){ scanf("%d%d",&n,&m); init(); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); uni(a,b); } int t=0; for(int i=1;i<=n;i++) if(fa[i]==i)t++; t = n - t; long long ans = 1; for(int i=1;i<=t;i++) { ans *= 2; } printf("%lld\n",ans); return 0; }