week 5图的基础

T1:危险系数

题目描述:

# [蓝桥杯 2013 国 C] 危险系数

地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

我们来定义一个危险系数 $DF(x,y)$:

对于两个站点 $x$ 和 $y(x\neq y),$ 如果能找到一个站点 $z$,当 $z$ 被敌人破坏后,$x$ 和 $y$ 不连通,那么我们称 $z$ 为关于 $x,y$ 的关键点。相应的,对于任意一对站点 $x$ 和 $y$,危险系数 $DF(x,y)$ 就表示为这两点之间的关键点个数。

本题的任务是:已知网络结构,求两站点之间的危险系数。

## 输入格式

输入数据第一行包含 $2$ 个整数 $n(2 \le n \le 1000)$,$m(0 \le m \le 2000)$,分别代表站点数,通道数。

接下来 $m$ 行,每行两个整数 $u,v(1 \le u,v \le n,u\neq v)$ 代表一条通道。

最后 $1$ 行,两个数 $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 年第四届国赛

思路:首先使用vector+数组存双向图,对于危险系数我的理解是想从这起点到终点点必须经过的中途的点(假设从起点到终点有n条通往的道路,这些所谓的关键点就是n条道路里面都经过的点,就像宿舍到工学馆,走到工学馆的路有很多条,但是前提你必须走到宿舍大门口(这个就是关键点)),主要看如何寻找到关键点。

找关键点:用dfs搜索从起点到终点的路的条数way,用cnt【i】数组记录这个搜索条数过程中经过这个点的次数,如果搜索完成后,若way==cnt【i】,说明这个就是它的一个关键点

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,head,last;
int way,cnt[1005],ans;//way记录从起点到终点路径条数,cnt代表某节点被历遍的次数 
vector <int> maze[1005];
int vis[1005];
void dfs(int x)
{
	if(x==last){
		way++;
		for(int i=1;i<=n;i++){
			if(vis[i])cnt[i]++;//如果这个节点在这条路上被访问,对应的cnt++
		}
		return;
	}
	for(int i=0;i<maze[x].size();i++){
		int nx=maze[x][i];
		if(!vis[nx]){
			vis[nx]=1;
			dfs(nx);
			vis[nx]=0;
		}
	}
	return; 
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		maze[x].push_back(y);
		maze[y].push_back(x);
	}//存图
	cin>>head>>last;
	dfs(head);
	if(!way){
		cout<<-1;
		return 0;
	}//如果没有路输出-1
	for(int i=1;i<=n;i++){
		if(way== cnt[i])ans++;//这个地方将终点也算进去了,终点不算关键点,所以输出的答案减去1
	}
	cout<<ans-1;
}

T2:图的历遍(借鉴大佬顶级逆向思维,反向存边)

题目描述:

给出 $N$ 个点,$M$ 条边的有向图,对于每个点 $v$,求 $A(v)$ 表示从点 $v$ 出发,能到达的编号最大的点。

## 输入格式

第 $1$ 行 $2$ 个整数 $N,M$,表示点数和边数。

接下来 $M$ 行,每行 $2$ 个整数 $U_i,V_i$,表示边 $(U_i,V_i)$。点用 $1,2,\dots,N$ 编号。

## 输出格式

一行 $N$ 个整数 $A(1),A(2),\dots,A(N)$。

## 样例 #1

### 样例输入 #1

```
4 3
1 2
2 4
4 3
```

### 样例输出 #1

```
4 4 3 4
```

## 提示

- 对于 $60\%$ 的数据,$1 \leq N,M \leq 10^3$。
- 对于 $100\%$ 的数据,$1 \leq N,M \leq 10^5$。

思路:(反向存图+dfs)题示为单向边,这边采用反向存边,考虑从较大点可以到达哪些点,这个较大值即为答案(从最大编号点n开始进行dfs历遍),每个点i能访问到的结点的A值都是i,每个点访问一次,这个A值就是最优的

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,ans[100006];
vector <int> maze[100006];
void dfs(int x,int y)//x代表编号为x的点,y为对应编号为x的点能到达的最大编号数
{
	if(ans[x])return;
	ans[x]=y;
	for(int i=0;i<maze[x].size();i++){
		int nx=maze[x][i];
		dfs(nx,y);
	} //这里感觉和连通块那个好像,都不需要回溯,一直走就完事
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){//第一步读取单向边,反向建边 
		int p,q;
		cin>>p>>q;
		maze[q].push_back(p); 
	}
	//第二步对每个点进行搜
	for(int i=n;i>=1;i--){
		dfs(i,i);	
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
	return 0;
 } 

T3:封锁阳光大学

题目描述:

曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

阳光大学的校园是一张由 $n$ 个点构成的无向图,$n$ 个点之间由 $m$ 条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

## 输入格式

第一行两个正整数,表示节点数和边数。
接下来 $m$ 行,每行两个整数 $u,v$,表示点 $u$ 到点 $v$ 之间有道路相连。

## 输出格式

仅一行如果河蟹无法封锁所有道路,则输出 `Impossible`,否则输出一个整数,表示最少需要多少只河蟹。

## 样例 #1

### 样例输入 #1

```
3 3
1 2
1 3
2 3
```

### 样例输出 #1

```
Impossible
```

## 样例 #2

### 样例输入 #2

```
3 2
1 2
2 3
```

### 样例输出 #2

```
1
```

## 提示

【数据规模】   
对于 $100\%$ 的数据,$1\le n \le 10^4$,$1\le m \le 10^5$,保证没有重边。

思路:dfs+染色;对于整张图所需要的最少蟹的数量问题可以分为n个小问题,即寻找到每一个相对独立的联通块(不包含只有一个点的情况,因为只有一个点,不需要蟹)所需的最少蟹数量然后相加

从标号为一的点,开始历遍,分为三种情况:

  1. 只有这个单独的一个点,无需加螃蟹(即对应的vector为空)
  2. 对于有联通的点now,先将now这个点染成1色(红色),再将now这个点所链接的点全部染成相反的颜色-1色(蓝色),对于这个联通块需要的最小蟹为min(1色的数量,-1色的数量),对于之前已经染过色的点,之后无需历遍,已经染过色的点代表已经联通起来了
  3. 对于无解的情况,就是在染色过程中,存在两个相连的点染的是同一种颜色

(最开始我只考虑了全是联通块的情况)

 

#include<bits/stdc++.h>
using namespace std;
//vis为0代表未染色 ,vis为-1为染成-1色,vis为1则染为1色 
int n,m,c01,c10,vis[100005],ans;//c01表示染成 -1色的数量,c10表示染成1色数量
vector <int> maze[100005];
void dfs(int x)
{
	if(vis[x]==-1)c01++;
	else c10++;
	for(int i=0;i<maze[x].size();i++){//将这个节点中所有联通块染色 
		int nx=maze[x][i];
		if(vis[nx]==vis[x]){//染色,染成同一颜色了
			cout<<"Impossible";
			exit(0);
		}
		if(vis[nx]!=0)continue;//如果已经染过色,跳过 
		vis[nx]=-vis[x];//把下一个节点染成相反颜色 
		dfs(nx); 
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int p,q;
		cin>>p>>q;
		maze[p].push_back(q);
		maze[q].push_back(p);
	}
	for(int i=1;i<=n;i++) //遍历图上的点
	{
		c01=0;c10=0;
		if(vis[i]!=0||maze[i].empty())continue;/*如果这个点在寻找联通块时候
		已经被标记了或者没有其他点与他链接,就不用再去dfs了*/
		vis[i]=-1;
		dfs(i);
		//50分痛改为100 
		ans+=min(c01,c10);
	} 
	//cout<<"-1色有"<<c01<<"  1色有"<<c10<<endl;
	//int ans=min(c01,c10);
	cout<<ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值