7-1 图的深度优先搜索I (100 分)
无向图 G 有 n 个顶点和 m 条边。求图G的深度优先搜索树(森林)以及每个顶点的发现时间和完成时间。每个连通分量从编号最小的结点开始搜索,邻接顶点选择顺序遵循边的输入顺序。
在搜索过程中,第一次遇到一个结点,称该结点被发现;一个结点的所有邻接结点都搜索完,该结点的搜索被完成。深度优先搜索维护一个时钟,时钟从0开始计数,结点被搜索发现或完成时,时钟计数增1,然后为当前结点盖上时间戳。一个结点被搜索发现和完成的时间戳分别称为该结点的发现时间和完成时间
输入格式:
第1行,2个整数n和m,用空格分隔,分别表示顶点数和边数, 1≤n≤50000, 1≤m≤100000.
第2到m+1行,每行两个整数u和v,用空格分隔,表示顶点u到顶点v有一条边,u和v是顶点编号,1≤u,v≤n.
输出格式:
第1到n行,每行两个整数di和fi,用空格分隔,表示第i个顶点的发现时间和完成时间1≤i≤n 。
第n+1行,1个整数 k ,表示图的深度优先搜索树(森林)的边数。
第n+2到n+k+1行,每行两个整数u和v,表示深度优先搜索树(森林)的一条边<u,v>,边的输出顺序按 v 结点编号从小到大。
输入样例:
在这里给出一组输入。例如:
6 5
1 3
1 2
2 3
4 5
5 6
输出样例:
在这里给出相应的输出。例如:
1 6
3 4
2 5
7 12
8 11
9 10
4
3 2
1 3
4 5
5 6
题目分析
dfs。
代码实现如下
#include <bits/stdc++.h>
using namespace std;
int num=1;
int arc;
int fi[50010],di[50010],visited[50010];
struct cmp{
bool operator ()(pair<int,int> a,pair<int,int> b){
return a.second>b.second;
}
};
priority_queue<pair<int,int>,vector<pair<int,int> >,cmp> q;
vector<int> e[50010];
void dfs(int u){
visited[u]=1;
fi[u]=num++;
for(int j=0;j<e[u].size();j++){
int v=e[u][j];
if(!visited[v]){
dfs(v);
arc++;
q.push({u,v});
}
}
di[u]=num++;
return ;
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
for(int i=1;i<=n;i++){
if(!visited[i])
dfs(i);
}
for(int i=1;i<=n;i++){
printf("%d %d\n",fi[i],di[i]);
}
printf("%d\n",arc);
while(!q.empty()){
pair<int,int> uv=q.top();
q.pop();
int u=uv.first;
int v=uv.second;
printf("%d %d\n",u,v);
}
return 0;
}
7-2 圆 (100 分)
二维平面上有n 个圆。请统计:这些圆形成的不同的块的数目。
圆形成的块定义如下: (1)一个圆是一个块; (2)若两个块有公共部分(含相切),则这两个块形成一个新的块,否则还是两个不同的块。
输入格式:
第1行包括一个整数n,表示圆的数目,n<=8000。
第2到n+1行,每行3 个用空格隔开的数x,y,r。(x,y)是圆心坐标,r 是半径。所有的坐标及半径都是不大于30000 的非负整数。
输出格式:
1个整数,表示形成的块的数目。
输入样例:
在这里给出一组输入。例如:
2
0 0 1
1 0 2
输出样例:
在这里给出相应的输出。例如:
1
题目分析
即考察并查集。值得注意的是,求两个圆相交时不应该开根号,用三个long long类型数储存比较数即可。
代码实现如下
#include <bits/stdc++.h>
using namespace std;
struct circle{
int x,y,r;
}c[8010];
int mark[8010];
int num;
int pan(circle a,circle b){
int x1=a.x,y1=a.y,r1=a.r,x2=b.x,y2=b.y,r2=b.r;
long d=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
long p=r1+r2;
p=p*p;
if(p>=d)return 1;
else return 0;
}
int main(){
int n;
scanf("%d",&n);
num=n;
for(int i=1;i<=n;i++){
int x0,y0,r0;
scanf("%d %d %d",&x0,&y0,&r0);
c[i].r=r0;
c[i].x=x0;
c[i].y=y0;
mark[i]=i;
}
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
if(pan(c[i],c[j])){
int a,b;
a=mark[i];
b=mark[j];
while(a!=mark[a])a=mark[a];
while(b!=mark[b])b=mark[b];
mark[b]=a;
}
}
}
for(int i=1;i<=n;i++){
if(mark[i]!=i){
num--;
}
}
printf("%d",num);
return 0;
}
7-3 供电 (100 分)
要给N个地区供电。每个地区或者建一个供电站,或者修一条线道连接到其它有电的地区。试确定给N个地区都供上电的最小费用。
输入格式:
第1行,两个个整数 N 和 M , 用空格分隔,分别表示地区数和修线路的方案数,1≤N≤10000,0≤M≤50000。
第2行,包含N个用空格分隔的整数P[i],表示在第i个地区建一个供电站的代价,1 ≤P[i]≤ 100,000,1≤i≤N 。
接下来M行,每行3个整数a、b和c,用空格分隔,表示在地区a和b之间修一条线路的代价为c,1 ≤ c ≤ 100,000,1≤a,b≤N 。
输出格式:
一行,包含一个整数, 表示所求最小代价。
输入样例:
在这里给出一组输入。例如:
4 6
5 4 4 3
1 2 2
1 3 2
1 4 2
2 3 3
2 4 3
3 4 4
输出样例:
在这里给出相应的输出。例如:
9
题目分析
每个供电站可以写一个自己到自己的边,取点权最小的作为起点,使用prim算法即可。
代码实现如下
#include <bits/stdc++.h>
using namespace std;
#define max 100010
int lowcost[max];
int mark[max];
long long sum;
vector<pair<long,int> >e[max];
void prim(int st,int n){
mark[st]=1;
for(int i=0;i<e[st].size();i++){
pair<long,int> wv=e[st][i];
long w=wv.first;
int v=wv.second;
if(lowcost[v]>w)lowcost[v]=w;
}
for(int i=1;i<n;i++){
int r=max,u=0;
for(int j=1;j<=n;j++){
if(lowcost[j]<r&&!mark[j]){
r=lowcost[j];
u=j;
}
}
mark[u]=1;
for(int l=0;l<e[u].size();l++){
pair<long,int> wv=e[u][l];
long w=wv.first;
int v=wv.second;
if(lowcost[v]>w&&!mark[v]){
lowcost[v]=w;
}
}
}
return ;
}
int main(){
int n,m,min=0;
lowcost[min]=max;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
lowcost[i]=x;
if(lowcost[min]>lowcost[i])min=i;
}
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
e[u].push_back({w,v});
e[v].push_back({w,u});
}
prim(min,n);
for(int i=1;i<=n;i++)sum+=lowcost[i];
printf("%lld\n",sum);
return 0;
}
7-4 发红包 (100 分)
新年到了,公司要给员工发红包。员工们会比较获得的红包,有些员工会有钱数的要求,例如,c1的红包钱数要比c2的多。每个员工的红包钱数至少要发888元,这是一个幸运数字。
公司想满足所有员工的要求,同时也要花钱最少,请你帮助计算。
输入格式:
第1行,两个整数n和m(n<=10000,m<=20000),用空格分隔,分别代表员工数和要求数。
接下来m行,每行两个整数c1和c2,用空格分隔,表示员工c1的红包钱数要比c2多,员工的编号1~n 。
输出格式:
一个整数,表示公司发的最少钱数。如果公司不能满足所有员工的需求,输出-1.
输入样例:
在这里给出一组输入。例如:
2 1
1 2
输出样例:
在这里给出相应的输出。例如:
1777
题目分析
很容易看出是一个拓扑序。
代码实现如下
#include <bits/stdc++.h>
using namespace std;
#define max 10010
int s[max];
int counter[max];
int sum;
vector<int> e[max];
queue<int> q;
int topo(int n){
for(int i=1;i<=n;i++){
for(int j=0;j<e[i].size();j++){
int v=e[i][j];
counter[v]++;
}
}
for(int i=1;i<=n;i++){
if(counter[i])continue;
else {q.push(i);s[i]=888;sum+=s[i];}
}
for(int i=1;i<=n;i++){
int t=q.front();
q.pop();
for(int j=0;j<e[t].size();j++){
int v=e[t][j];
counter[v]--;
if(counter[v])continue;
else {q.push(v);s[v]=s[t]+1;sum+=s[v];}
}
}
if(q.empty())return 1;
else return 0;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++){
int u,v;
scanf("%d %d",&u,&v);
e[u].push_back(v);
}
if(topo(n)) printf("%d",sum);
else printf("-1");
}