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
#include<bits/stdc++.h>
using namespace std;
int Clock;
int line;
int visit[50010];
struct time{
int start;
int rear;
}a[50010];
struct vex{
int x;
int y;
bool operator<(const struct vex&a)const{
return y<a.y;
}
}b[50010];
struct edge{
int Veradj;
struct edge* link;
};
struct Vertex{
edge* rear;
edge* adjacent;
}head[50010];
void DFS(Vertex* head,int v,int *visit){
if(visit[v]) return;
visit[v]=1;
a[v].start=++Clock;
edge* p=head[v].adjacent;
while(p){
if(visit[p->Veradj]==0){
b[line].x=v;
b[line++].y=p->Veradj;
DFS(head,p->Veradj,visit);
}
p=p->link;
}
a[v].rear=++Clock;
}
int main(){
int n,m,u1,v1,v;
scanf("%d%d",&n,&m);
int i,j;
for(i=1;i<=n;i++){
head[i].adjacent=NULL;
head[i].rear=NULL;
}
for(i=1;i<=m;i++){
scanf("%d%d",&u1,&v1);
edge* p=new edge;
p->Veradj=v1;
if(!head[u1].adjacent){
head[u1].adjacent=p;
head[u1].rear=p;
p->link=NULL;
}
else{
head[u1].rear->link=p;
p->link=NULL;
head[u1].rear=p;
}
p=new edge;
p->Veradj=u1;
if(!head[v1].adjacent){
head[v1].adjacent=p;
head[v1].rear=p;
p->link=NULL;
}
else{
head[v1].rear->link=p;
p->link=NULL;
head[v1].rear=p;
}
}
for(i=1;i<=n;i++)
visit[i]=0;
for(i=1;i<=n;i++)
DFS(head,i,visit);
for(i=1;i<=n;i++)
printf("%d %d\n",a[i].start,a[i].rear);
printf("%d",line);
sort(b,b+line);
for(i=0;i<line;i++){
printf("\n%d %d",b[i].x,b[i].y);
}
}
我的思路:
这个题是DFS的典型题,用递归算法,需要注意的是时间戳的加入位置和每条边的起始点和终点的加入位置。最后按v的大小输出的时候,我开始用map中的value来比较的,但是内存超限,之后才知道可以用sort来比较会比较快(但是需要重载运算符)。
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
#include<bits/stdc++.h>
using namespace std;
int father[8010];
int n;
struct circle{
double x;
double y;
double r;
}a[8010];
void make_set(int v){
for(int i=1;i<=v;i++)
father[i]=i;
}
int Find(int v){
if(father[v]==v) return v;
else return Find(father[v]);
}
void Union(int x,int y){
father[Find(x)]=Find(y);
}
int compare(double x1,double y1,double r1,double x2,double y2,double r2){
if((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)>(r1+r2)*(r1+r2)) return 0;
else return 1;
}
int main(){
cin>>n;
int i,j;
make_set(n);
for(i=1;i<=n;i++)
cin>>a[i].x>>a[i].y>>a[i].r;
for(i=2;i<=n;i++)
for(j=1;j<=i-1;j++)
if(compare(a[i].x,a[i].y,a[i].r,a[j].x,a[j].y,a[j].r)){
Union(i,j);
}
int num=0;
for(i=1;i<=n;i++)
if(father[i]==i)
num++;
cout<<num;
}
我的思路:
这个题是并查集的模板题,直接复制粘贴就行。
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
#include<bits/stdc++.h>
using namespace std;
#define MAX 1000000;
struct edge{
int Veradj;
int cost;
struct edge* link;
};
struct Vertex{
edge* adjacent;
}head[10010];
int lowcost[10010];
int mark[10010];
int main(){
int pp[10010];
int n,m;
cin>>n; cin>>m;
int i,j;
int a,b,c;
int minn,t;
cin>>pp[1];
minn=pp[1]; t=1;
for(i=2;i<=n;i++){
cin>>pp[i];
if(pp[i]<minn){
t=i;
minn=pp[i];
}
}
for(i=1;i<=n;i++)
head[i].adjacent=NULL;
for(i=1;i<=m;i++){
cin>>a>>b>>c;
edge* p=head[a].adjacent;
edge* q=new edge;
q->Veradj=b;
q->cost=c;
q->link=p;
head[a].adjacent=q;
p=head[b].adjacent;
q=new edge;
q->Veradj=a;
q->cost=c;
q->link=p;
head[b].adjacent=q;
}
long long mindist,u,v,k;
for(i=1;i<=n;i++){
lowcost[i]=pp[i];
mark[i]=0;
}
mark[t]=1;
for(edge* p=head[t].adjacent;p;p=p->link){
k=p->Veradj;
if(lowcost[k]>p->cost)
lowcost[k]=p->cost;
}
for(j=1;j<n;j++){
mindist=MAX; //确定轻边
for(i=1;i<=n;i++)
if(lowcost[i]<mindist&&mark[i]==0){
mindist=lowcost[i];
u=i;
}
mark[u]=1;
for(edge* p=head[u].adjacent;p;p=p->link){
v=p->Veradj;
if(p->cost<lowcost[v]&&mark[v]==0)
lowcost[v]=p->cost;
}
}
long long sum=0;
for(i=1;i<=n;i++)
sum+=lowcost[i];
cout<<sum;
}
我的思路:
这个题是最小支撑树的变形题,我用的是Prim算法,lowcost[i]的初值是每个地点建工电站的代价,其实这种做法是增加了一个虚点,此点和其他点共同组成了一个连通分量。之后就看边权和对应点的权值大小,取小的那一个,再按照Prim算法来做就可以了。
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
作者
谷方明
单位
吉林大学
代码长度限制
16 KB
时间限制
50 ms
内存限制
#include<bits/stdc++.h>
using namespace std;
struct edge{
int Veradj;
struct edge* link;
};
struct Vertex{
edge* adjacent;
}head[10010];
int t=888;
int sum;
int main(){
int c1,c2;
int n,m;
cin>>n>>m;
int i,j,x1=0,x2=0;
for(i=1;i<=n;i++)
head[i].adjacent=NULL;
for(i=1;i<=m;i++){
cin>>c1>>c2;
edge* p=head[c2].adjacent;
edge* q=new edge;
q->Veradj=c1;
q->link=p;
head[c2].adjacent=q;
}
queue<int>qq;
int Count[10010];
for(i=1;i<=n;i++)
Count[i]=0;
for(i=1;i<=n;i++)
for(edge* p=head[i].adjacent;p;p=p->link)
Count[p->Veradj]++;
for(i=1;i<=n;i++)
if(Count[i]==0){
qq.push(i);
x1++;
}
sum+=x1*t;
for(i=1;i<=n;i++){
if(qq.empty()){
cout<<"-1";
return 0;
}
j=qq.front();
qq.pop();
x1--;
for(edge* p=head[j].adjacent;p;p=p->link){
int k=p->Veradj;
Count[k]--;
if(Count[k]==0){
qq.push(k);
x2++;
}
}
if(!x1){
sum+=(++t)*x2;
x1=x2;
x2=0;
}
}
cout<<sum;
}
我的思路:
这道题也算是拓扑排序的模板题了吧,我用的是队列,就是每层是一个价格,这时需要加两个标记,一个记录上层的数目,一个记录本层的数目,注意一定要更新这两个值!!