A : Wireless Network POJ - 2236
题意:并查集,可以有查询和修复操作
题解:并查集
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; #define N 1110 int d; bool use[N]; struct node { int pre; int x, y; }p[N]; int find(int x) { return x == p[x].pre ? x : find(p[x].pre); } void join(const node p1, const node p2) { int root1, root2; root1 = find(p1.pre); root2 = find(p2.pre); if(root1 != root2) if((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) <= d * d) p[root2].pre = root1; } int main() { //freopen("Input.txt", "r", stdin); int num; char ope; int ok; int from, to; scanf("%d%d", &num, &d); for(int i = 1; i <= num; ++i) p[i].pre = i; memset(use, false, sizeof(use)); for(int i = 1; i <= num; ++i) scanf("%d%d", &p[i].x, &p[i].y); while(scanf(" %c", &ope) != EOF) { if(ope == 'O') { scanf("%d", &ok); use[ok] = true; for(int i = 1; i <= num; ++i) if(use[i] && i != ok) join(p[i], p[ok]); } else { scanf("%d%d", &from, &to); if(find(from) == find(to)) printf("SUCCESS "); else printf("FAIL "); } } return 0; }
B:The Suspects POJ - 1611
题意:0节点为携带病毒人,有一些团队,一个人可以在多个团队,一个团队一个有病毒,其余都感染,问一共几人感染病毒
题解:寻找0号根结点对应的sum值
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<cstdlib> #include <vector> #include<queue> using namespace std; #define ll long long #define llu unsigned long long #define INF 0x3f3f3f3f #define PI acos(-1.0) const int maxn = 1e5+5; const int mod = 1e9+7; int father[maxn],sum[maxn]; void init(int n) { for(int i=0;i<n;i++) { father[i] = i; sum[i] = 1; } } int find(int x) { if(father[x] == x) return x; else return father[x] = find(father[x]); } void combine(int x,int y) { int xx = find(x); int yy = find(y); if(xx != yy) { father[xx] = yy; sum[yy] += sum[xx]; } } int main() { int n,m; while(~scanf("%d %d",&n,&m)) { init(n); if(n == 0 && m == 0) break; for(int i=0;i<m;i++) { int x; scanf("%d",&x); int one,two; scanf("%d",&one); for(int j=1;j<x;j++) { scanf("%d",&two); combine(one,two); } } printf("%d ",sum[find(0)]); } }
C:How Many Tables hdu-1213
题意:认识的人可以坐在一张桌子,也可以间接认识,问需要几张桌子
思路:并查集之后找有几个集
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<map> #include<cstdlib> #include <vector> #include<queue> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1e3+5; int father[maxn]; void init(int n) { for(int i=1;i<=n;i++) father[i] = i; } int find(int x) { if(father[x] == x) return x; else return father[x] = find(father[x]); } void combine(int x,int y) { x = find(x); y = find(y); if(x != y) father[x] = y; } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d %d", &n,&m); init(n); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); combine(a,b); } int res = 0; for(int i=1;i<=n;i++) if(i == find(i)) res++; printf("%d ",res); } }
D:How Many Answers Are Wrong hdu-3038
思路:带权并查集
从第u个数到第v个数的和其实可以理解为,第u-1个数到v个数之间的和。那么,就可以把和当成一种关系,利用带权并查集来维护这种关系。u-1节点为根,v的权值为第u-1个数到v个数之间的和。这里需要理解一下的是,在Find函数和unite(合并)函数里面,有两个关系域的转移方程(暂且这么叫他吧)
①p[x].relation+=p[tmp].relation
②p[root2].relation=p[x].relation+relation-p[y].relation
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<map> #include<cstdlib> #include <vector> #include<queue> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 2e5+5; struct node { int father; int w; }; node p[maxn]; int n,m,ans; void init() { ans = 0; for(int i=1;i<=n;i++) { p[i].father = i; p[i].w = 0; } } int find(int x) { if(x == p[x].father) return x; int tmp = p[x].father; p[x].father = find(tmp); p[x].w += p[tmp].w; return p[x].father; } void combine(int x,int y,int w) { int x1 = find(x); int y1 = find(y); if(x1 != y1) { p[y1].father = x1; p[y1].w = p[x].w + w -p[y].w; } else { if(p[y].w - p[x].w != w) ans ++; } } int main() { while(~scanf("%d %d",&n,&m)) { int u,v,w; init(); for(int i=0;i<m;i++) { scanf("%d %d %d",&u,&v,&w); u -= 1; combine(u,v,w); } printf("%d ",ans); } }
E - 食物链 POJ - 1182
题意:A吃B,B吃C,C吃A,给出一些话,问有几句假话
题解:可开3倍数组,也可以用结构体,关系域用向量加减操作
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<map> #include<cstdlib> #include<vector> #include<string> #include<queue> using namespace std; #define ll long long #define llu unsigned long long #define INF 0x3f3f3f3f const double PI = acos(-1.0); const int maxn = 5e4+10; const int mod = 1e9+7; struct node { int pre; int relation; //0:与根节点同类 1:被根节点吃 2:吃根节点 }p[maxn]; int find(int x) { int temp; if(x == p[x].pre) return x; temp = p[x].pre; p[x].pre = find(temp); p[x].relation = (p[x].relation + p[temp].relation) % 3; return p[x].pre; } int main() { int n,k; int ope,a,b; int root1,root2; int sum = 0; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { p[i].pre = i; p[i].relation = 0; } while(k--) { scanf("%d%d%d",&ope,&a,&b); if(max(a,b) > n) { sum++; continue; } if(ope == 2 && a == b) { sum++; continue; } root1 = find(a); root2 = find(b); if(root1 != root2) { p[root2].pre = root1; p[root2].relation = (3+(ope - 1) + p[a].relation - p[b].relation) % 3; } else { if(ope == 1 &&p[a].relation != p[b].relation) { sum++; continue; } if(ope == 2 &&((3 - p[a].relation + p[b].relation) % 3 != ope -1)) { sum++; continue; } } } printf("%d ",sum); }
F - True Liars POJ - 1417
题目大意:给你p1个好人和p2个坏人,编号为1-p1+p2,然后给你n中操作
x1 x2 no:x1说x2不是好人
x1 x2 yes:x1说x2是好人
在这里好人说的总是对的,坏人说的总是坏的,然后问你最后能不能唯一确定哪些是好人,并输出不能就输出no
题目思路:开始看到这题很好想到的就是用并查集,如果是yes的话说明他们同类,no的话说明不是同类,但这样只能分出几个大集合来,每个集合又分成两个小集合表示两种类型的个数,而我们要求的是在所有大集合中选出一个小集合然后加起来看能不能组 合成p1,并且要唯一,这里我们可以想到用背包来做,dp[i][j]表示前i个大集合好人为j个的方案数,第i种状态只能是有第i-1种状态而来,我们用w0[i],w1[i]表示第i个集合两个小集合的个数 ,所以dp[i][j]可以由dp[i-1][j-w0[]i]和dp[i-1][j-w1[i]]得来,这样我们只需判断dp[cnt][p1]是否等于1,这题还有麻烦的就是输出好人的编号,这里我们可以利用边的权值,w0存的全是权值为0的w1存的全是权值为1的,然后最后只需判断下第i个集合是由w0还是w1组合而来,这里可以利用dp[i-1][p1-w0[i]]的值来判断,如果等于1则表示是由w0组合否则就是w1,这个不难理,dp[i-1][p1-w0[i]]==1可以得到dp[i][p1]==1,应为这里已经表示答案唯一,所以只有一种情况!
此题题意题解转载自: https://blog.csdn.net/qq_34731703/article/details/54603652
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<cstdlib> #include <vector> #include <set> #include<queue> using namespace std; #define ll long long #define llu unsigned long long #define INF 0x3f3f3f3f #define PI acos(-1.0) const int maxn = 1e3+5; const ll mod = 1e9+7; int pre[maxn],rk[maxn],p[maxn],vis[maxn]; int w0[maxn],w1[maxn],dp[maxn][maxn]; int n,p1,p2; void init() { for(int i=1;i<=p1+p2;i++) { pre[i] = i; rk[i] = 0; vis[i] = 0; w0[i] = w1[i] = 0; } memset(dp,0,sizeof dp); } int find(int x) { if(x != pre[x]) { int temp = pre[x]; pre[x] = find(pre[x]); rk[x] = rk[x] ^ rk[temp]; } return pre[x]; } void combine(int x,int y,int k) { int xx = find(x); int yy = find(y); if(xx != yy) { pre[xx] = yy; rk[xx] = rk[x] ^ rk[y] ^ k; } } int main() { while(scanf("%d %d %d",&n,&p1,&p2) && (n + p1 + p2)) { init(); for(int i=0;i<n;i++) { int a,b; char str[10]; scanf("%d %d %s",&a,&b,str); if(str[0] == 'y') combine(a,b,0); else combine(a,b,1); } int cnt = 1; for(int i=1;i<=p1+p2;i++) { if(!vis[i]) { int fa = find(i); for(int j=i;j<=p1+p2;j++) { if(find(j) == fa && !vis[j]) { vis[j] = 1; if(rk[j] == 0) w0[cnt]++; else w1[cnt]++; } } p[cnt] = fa; cnt++; } } dp[0][0] = 1; for(int i=1;i<cnt;i++) { int minn = min(w0[i],w1[i]); for(int j=p1;j>=minn;j--) { if(dp[i-1][j-w0[i]]) dp[i][j] += dp[i-1][j-w0[i]]; if(dp[i-1][j-w1[i]]) dp[i][j] += dp[i-1][j-w1[i]]; } } if(dp[cnt-1][p1] != 1) { puts("no"); continue; } int ans[maxn]; int num = 0; int good = p1; for(int i=cnt-1;i>=1;i--) { if(dp[i-1][good-w0[i]] == 1) { for(int j=1;j<=p1+p2;j++) if(find(j) == p[i] && rk[j] == 0) ans[num++]=j; good -= w0[i]; } else { for(int j=1;j<=p1+p2;j++) if(find(j) == p[i] && rk[j] == 1) ans[num++] = j; good -= w1[i]; } } sort(ans,ans + num); for(int i=0;i<num;i++) printf("%d ",ans[i]); puts("end"); } return 0; }
G - Supermarket POJ - 1456
题意:超市里有n个产品要卖,每个产品都有一个截至时间d(从开始卖时算起),只有在这个截至时间之前才能卖出并且获得率润dy。
题解:先将产品按照价格从高到低排序,之后用并查集来寻找还有的时间
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<cstdlib> #include <vector> #include <set> #include<queue> using namespace std; #define ll long long #define llu unsigned long long #define INF 0x3f3f3f3f #define PI acos(-1.0) const int maxn = 1e4+5; const ll mod = 1e9+7; struct node { int p,d; }a[maxn]; int pre[maxn]; bool cmp(node a,node b) { return a.p > b.p; } int find(int x) { if(pre[x] == -1) return x; else return pre[x] = find(pre[x]); } int main() { int n; while(~scanf("%d",&n)) { memset(pre,-1,sizeof pre); for (int i = 0; i < n; i++) scanf("%d %d",&a[i].p,&a[i].d); sort(a,a+n,cmp); int ans = 0; for(int i=0;i<n;i++) { int t = find(a[i].d); // cout<<t<<endl; if(t) { ans += a[i].p; pre[t] = t-1; //cout<<"pre[t]"<<t-1<<endl; } } printf("%d ",ans); } return 0; }
M - 小希的迷宫 HDU - 1272
题意:给一张地图,要求任意的两个房间之间只有一条路,是的输出Yes,不是的输出No
思路:使用并查集,当发现两个房间的根节点不一样的话就将两个房间连接起来,如果两个房间的根节点是一样的话,说明成环了,就不是。同时,也有可能不是联通的,需要验证,就是只有一个根节点在所有的点中,还有一个特判,直接输入0 0 的话输出的是Yes
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<sstream> #include<cmath> #include<stack> #include<cstdlib> #include <vector> #include <set> #include<queue> #include<map> using namespace std; #define ll long long #define llu unsigned long long #define INF 0x3f3f3f3f #define PI acos(-1.0) const int maxn = 1e5+5; const ll mod = 1e9+7; const double eps = 1e-8; int pre[maxn]; int vis[maxn]; void init() { for(int i=1;i<maxn;i++) pre[i] = i; memset(vis,0,sizeof vis); } int find(int x) { if(x == pre[x]) return x; else return pre[x] = find(pre[x]); } void combine(int x,int y) { int fx = find(x); int fy = find(y); if(fx != fy) pre[fy] = fx; } bool same(int x,int y) { int fx = find(x); int fy = find(y); return fx == fy; } int main() { int a,b; while(scanf("%d%d",&a,&b)) { init(); vis[a] = vis[b] = 1; if(a == -1 && b == -1) break; if(a==0 && b==0) { puts("Yes"); continue; } combine(a,b); int x,y; int flag = 0; while(scanf("%d%d",&x,&y)) { if(x==0 && y==0) { int ans = 0; for(int i=1;i<maxn;i++) if(vis[i] && pre[i]==i) { ans++; //cout<<i<<endl; } if(flag == 0 && ans == 1) puts("Yes"); else puts("No"); break; } vis[x] = vis[y] = 1; if(!same(x,y)) combine(x,y); else flag = 1; } } }