本周学习了图
图可以分为单向图和无向图
图的存储:
我目前学会的:
1.二维数组
缺点:容易爆掉
所以一般使用
2,vertor(如果不读入就是NULL空,不占用空间不会爆掉)
入读双向图
for(int k=1;k<=m;k++)
{
cin>>x>>y;
didao[x].push_back(y);
didao[y].push_back(x);
}
读入单项图
for(int k=1;k<=m;k++)
{
cin>>x>>y;
didao[x].push_back(y);
}
而图的查询一般使用DFS和BFS
因此在查询图的时候要考虑这两种方法
例题:
题目背景
抗日战争时期,冀中平原的地道战曾发挥重要作用。
题目描述
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数 DF(x,y)DF(x,y):
对于两个站点 xx 和 y(x\neq y),y(x=y), 如果能找到一个站点 zz,当 zz 被敌人破坏后,xx 和 yy 不连通,那么我们称 zz 为关于 x,yx,y 的关键点。相应的,对于任意一对站点 xx 和 yy,危险系数 DF(x,y)DF(x,y) 就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。
输入格式
输入数据第一行包含 22 个整数 n(2 \le n \le 1000)n(2≤n≤1000),m(0 \le m \le 2000)m(0≤m≤2000),分别代表站点数,通道数。
接下来 mm 行,每行两个整数 u,v(1 \le u,v \le n,u\neq v)u,v(1≤u,v≤n,u=v) 代表一条通道。
最后 11 行,两个数 u,vu,v,代表询问两点之间的危险系数 DF(u,v)DF(u,v)。
输出格式
一个整数,如果询问的两点不连通则输出 -1−1。
输入输出样例
输入 #1复制
7 6 1 3 2 3 3 4 3 5 4 5 5 6 1 6输出 #1复制
2说明/提示
时限 1 秒, 64M。蓝桥杯 2013 年第四届国赛
本题采用DFS进行深度优先查询
在图中查找从起点到终点 的条数
在走到点时
利用book的记录,找到本条路径都走过了哪些点
最后通过遍历每个点总记录数
若总记录数==路径数
说明每次都走过这个点,
这个点就是关键危险点
易错:起点和终点要排除,二者也满足上述需求
AC代码:
#include<bits/stdc++.h>
using namespace std;
int x,y,n,m,xp,ep,total;
int book[1005]={0};
vector <int> didao[1005];
int step[1005]={0};
void dfs(int p)
{
if(p==ep)
{
for(int k=1;k<=n;k++)
{
if(book[k]==1)
step[k]++;
}
total++;
return;
}
for(int i=0;i<didao[p].size();i++)
{
int next=didao[p][i];
if(book[next]==0)
{book[next]=1;
dfs(next);
book[next]=0;
}
}
return;
}
int main()
{
cin>>n>>m;
for(int k=1;k<=m;k++)
{
cin>>x>>y;
didao[x].push_back(y);
didao[y].push_back(x);
}
cin>>xp>>ep;
book[xp]=1;
int ans=-2;
dfs(xp);
for(int k=1;k<=n;k++)
{
if(step[k]==total)
ans++;
}
cout<<ans;
}
第二题:
题目描述
给出 NN 个点,MM 条边的有向图,对于每个点 vv,求 A(v)A(v) 表示从点 vv 出发,能到达的编号最大的点。
输入格式
第 11 行 22 个整数 N,MN,M,表示点数和边数。
接下来 MM 行,每行 22 个整数 U_i,V_iUi,Vi,表示边 (U_i,V_i)(Ui,Vi)。点用 1,2,\dots,N1,2,…,N 编号。
输出格式
一行 NN 个整数 A(1),A(2),\dots,A(N)A(1),A(2),…,A(N)。
输入输出样例
输入 #1复制
4 3 1 2 2 4 4 3输出 #1复制
4 4 3 4说明/提示
- 对于 60\%60% 的数据,1 \leq N,M \leq 10^31≤N,M≤103。
- 对于 100\%100% 的数据,1 \leq N,M \leq 10^51≤N,M≤105。
本题我使用了正向DFS 结果一半的测试点都超出了时间限制
查看0题解才发现可以逆向DBS
并且这样可以不用回头看
即book记录不用重新置0.
所以我们从n到1枚举起点
如果一个点被标记过,也就是说这个点有答案
和该点相连的所有点都被标记过,因此无需搜索这个点。
在遇到有答案的点是跳过即可
ac代码:
#include<bits/stdc++.h>
using namespace std;
int x,y,n,m,xp,ep,total;
int maxx[100005]={0};
int book[100005]={0};
vector <int> didao[100005];
void dfs(int p,int ori)
{ if(book[p]==1)
return;
maxx[p]=ori;
book[p]=1;
for(int i=0;i<didao[p].size();i++)
{
int next=didao[p][i];
dfs(next,ori);
}
return;
}
int main()
{
cin>>n>>m;
for(int k=1;k<=m;k++)
{
cin>>x>>y;
didao[y].push_back(x);
}
for(int k=n;k>=1;k--)
{
dfs(k,k);}
cout<<maxx[1];
for(int k=2;k<=n;k++)
cout<<" "<<maxx[k];
}
第三题封锁阳光大学
题目描述
曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。
阳光大学的校园是一张由 nn 个点构成的无向图,nn 个点之间由 mm 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。
询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。
输入格式
第一行两个正整数,表示节点数和边数。 接下来 mm 行,每行两个整数 u,vu,v,表示点 uu 到点 vv 之间有道路相连。
输出格式
仅一行如果河蟹无法封锁所有道路,则输出
Impossible
,否则输出一个整数,表示最少需要多少只河蟹。输入输出样例
输入 #1复制
3 3 1 2 1 3 2 3输出 #1复制
Impossible输入 #2复制
3 2 1 2 2 3输出 #2复制
1说明/提示
【数据规模】
对于 100\%100 % 的数据,1\le n \le 10^41≤n≤104,1\le m \le 10^51≤m≤105,保证没有重边。
思路:难点是 思路寻找
使用涂色法(①每一条边所连接的点中,至少要有一个被选中。②每一条边所连接的两个点,不能被同时选中。)
先将所有点设成-1
通过 ^ 实现0和1的转化
int t=0;
t=t^1---->t=1
再次
t=t^!----->t=0
这样改变颜色
impossible条件
发下下次要涂色的点和相邻的节点颜色一样
则输出impossible。
细节:因为河蟹数实际上就是一种颜色的节点数
因此要通过min函数找到两种节点哪种颜色最少
最少节点即为answer
AC代码、
#include<bits/stdc++.h> using namespace std; int x,y,n,q,an; int m[100005]={-1}; int book[100005]={0}; vector <int> didao[100005]; void dfs(int p,int t) { if(m[p]!=-1&&m[p]!=t) { cout<<"Impossible"; exit(0); } else if(m[p]==t) return; m[p]=t; book[p]=1; an++; for(int k=0;k<didao[p].size();k++) dfs(didao[p][k],t^1); } int main() { cin>>n>>q; for(int k=1;k<=q;k++) { cin>>x>>y; didao[y].push_back(x); didao[x].push_back(y); } int ans=0; for(int i=1;i<=n;i++) { if(book[i]==0) {an=0; memset(m,-1,sizeof(m)); dfs(i,0); int t=0; for(int i=1;i<=n;i++) { if(m[i]==1) t++;} ans=ans+min(t,an-t); } } cout<<ans; }