P3367 【模板】并查集
思路:
1.以数组a标记数字所在集合,若两数在同一集合中则数组a中数字一致
2.分z为1和2两种情况判断(小心部分特殊情况)
代码:
#include<bits/stdc++.h>
using namespace std;
int a[10005];
int main(){
int n,m,x,y,z,f=1;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>z>>x>>y;
if(z==1){
if(a[x]!=0){
if(a[y]!=0){
int t=a[y];
for(int j=1;j<=n;j++){
if(a[j]==t)
a[j]=a[x];
}
}
else{
a[y]=a[x];
}
}
else if(a[y]!=0){
if(a[x]!=0){
int t=a[x];
for(int j=1;j<=n;j++){
if(a[j]==t)
a[j]=a[y];
}
}
else{
a[x]=a[y];
}
}
else{
a[x]=a[y]=f;
f++;
}
}
else if(z==2){
if(a[x]!=0&&a[x]==a[y]||x==y)
cout<<"Y"<<endl;
else
cout<<"N"<<endl;
}
}
}
P8604 [蓝桥杯 2013 国 C] 危险系数
思路:
1.dfs()查找开始到目标的所有路径,每条路径中经过一个点就标记+1
2.最后标记数等于路径数的点就是关键点
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005],bj[1005],c[1005];
int n,m,u,v,s=0,s1=0;
void dfs(int b){
if(b==v){
s++;
for(int i=1;i<=n;i++){
if(bj[i]==1)
c[i]++;
}
}
else{
for(int i=1;i<=n;i++){
if(a[b][i]==1&&bj[i]==0){
bj[i]=1;
dfs(i);
bj[i]=0;
}
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>u>>v;
a[u][v]=a[v][u]=1;
}
cin>>u>>v;
dfs(u);
if(s>0){
for(int i=1;i<=n;i++){
if(c[i]==s)
s1++;
}
}
cout<<s1-1;
}
P1330 封锁阳光大学
思路:
1.如果一条边的一端有河蟹,那么另一端必然没有;而如果一端没有河蟹,那么另一端一定有才能封锁道路
2.利用邻接表结构广度搜索遍历每一条边,遍历每条边(至少一端已经染色),如果另一端没有染色,则染上和这一端相反的颜色;如果都已经染色,判断是否相反,若不是就一定Impossible
代码:
#include<bits/stdc++.h>
using namespace std;
int a[10005],bj[10005],sum[3];
int n,m,u,v,bian=0,s=0;
struct edge{
int p;
int p1;
}b[200005];
void add(int u1,int v1){
bian++;
b[bian].p=v1;
b[bian].p1=a[u1];
a[u1]=bian;
}
queue<int>q;
int bfs(int x){
bj[x]=1;
sum[1]=1;
sum[2]=0;
q.push(x);
while(!q.empty()){
u=q.front();
q.pop();
for(int i=a[u];i;i=b[i].p1){
v=b[i].p;
if(bj[v]==bj[u])
return 1;
if(bj[v]==0){
if(bj[u]==1)
bj[v]=2;
else
bj[v]=1;
sum[bj[v]]++;
q.push(v);
}
}
}
return 0;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++){
if(bj[i])
continue;
if(bfs(i)){
cout<<"Impossible";
return 0;
}
else
s+=min(sum[1],sum[2]);
}
cout<<s;
return 0;
}
P3916 图的遍历
思路:
1.按题目每次可以考虑编号较大的点可以反向到达哪些点,进行反向建边
2.使用dfs搜索,循环从n到1,则每个点i能访问到的结点的值都是i,在每个点访问一次,这个值都是最优的
代码:
#include<bits/stdc++.h>
using namespace std;
int a[100005],d[100005];
int n,m,u,v,t;
struct edge{
int p;
int q;
}b[100005];
void add(int x,int y){
b[++t]=(edge){y,a[x]};
a[x]=t;
}
void dfs(int x,int y){
d[x]=y;
for(int i=a[x];i!=0;i=b[i].q){
if(d[b[i].p]==0){
dfs(b[i].p,y);
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u>>v;
add(v,u);
}
for(int i=n;i>=1;i--){
if(d[i]==0)
dfs(i,i);
}
for(int i=1;i<=n;i++){
cout<<d[i];
if(i==n)
cout<<"\n";
else
cout<<" ";
}
}
P1119 灾后重建
思路:
1.本题目中N个非负整数t0,t1,…,tN−1,表示了每个村庄重建完成的时间,村庄重建时间递增,接下来Q行,每行3个非负整数x,y,t,询问在第t天,从村庄x到村庄y的最短路径长度为多少,数据保证了t是不下降的。
2.判断天数:对于不能到达的情况,判断给出的两个点是否有到目前没有修好。而对最短距离继续使用floyd,枚举的k代表中间接口,不把不能走的点作为k,保证目前这一天就对于其他能走的点没有影响。
代码:
#include<bits/stdc++.h>
using namespace std;
int t[205],a[205][205];
int x,y,z,n,m,q,k;
int main(){
cin>>n>>m;
memset(a,0x3f,sizeof(a));
for(int i=0;i<n;i++){
cin>>t[i];
a[i][i]=0;
}
for(int i=1;i<=m;i++){
cin>>x>>y>>z;
a[x][y]=a[y][x]=z;
}
cin>>q;
for(int i=1;i<=q;i++){
cin>>x>>y>>z;
while(t[k]<=z&&k<n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}
}
k++;
}
if(x>=k||y>=k)
cout<<"-1"<<endl;
else
cout<<a[x][y]<<endl;
}
}