JLU数据结构第四次上机实验
7-1 连通分量
代码长度限制 16 KB
时间限制 200 ms
内存限制 10 MB
题目描述
无向图 G 有 n 个顶点和 m 条边。求 G 的连通分量的数目。
输入格式
第1行,2个整数n和m,用空格分隔,分别表示顶点数和边数, 1≤n≤50000, 1≤m≤100000.
第2到m+1行,每行两个整数u和v,用空格分隔,表示顶点u到顶点v有一条边,u和v是顶点编号,1≤u,v≤n.
输出格式
1行,1个整数,表示所求连通分量的数目。
输入样例
在这里给出一组输入。例如:
6 5
1 3
1 2
2 3
4 5
5 6
输出样例
在这里给出相应的输出。例如:
2
思路
用树维护一个并查集,最初每个数据视为一个集合,即一个连通分量,每次合并成功就代表减少一个连通分量
代码
#include<iostream>
using namespace std;
int findf(int *f,int nd,int&nf){
while(f[nd]!=nd){
nd=f[nd];
nf++;
}
return nd;
}
int main(){
int n=0,m=0;
scanf("%d %d",&n,&m);
int*f=new int[n+1];
for(int i=1;i<n+1;i++)f[i]=i;
int m1,m2,f1,f2;
int N=n;
for(int i=0;i<m;i++){
scanf(" %d %d",&m1,&m2);
int nf1=0,nf2=0;
f1=findf(f,m1,nf1);
f2=findf(f,m2,nf2);
if(f1!=f2){
if(nf1>nf2)f[f2]=f1;
else f[f1]=f2;
N--;
}
}
printf("%d",N);
return 0;
}
7-2 整数拆分
代码长度限制 16 KB
时间限制 100 ms
内存限制 1 MB
题目描述
整数拆分是一个古老又有趣的问题。请给出将正整数 n 拆分成 k 个正整数的所有不重复方案。例如,将 5 拆分成 2 个正整数的不重复方案,有如下2组:(1,4)和(2,3)。注意(1,4) 和(4,1)被视为同一方案。每种方案按递增序输出,所有方案按方案递增序输出。
输入格式
1行,2个整数n和k,用空格分隔, 1≤k≤n≤50.
输出格式
若干行,每行一个拆分方案,方案中的数用空格分隔。
最后一行,给出不同拆分方案的总数。
输入样例
在这里给出一组输入。例如:
5 2
输出样例
输出样例:
在这里给出相应的输出。例如:
1 4
2 3
2
思路
回溯法,递归实现。将拆分出的数据用vector a保存,每层递归处理a的一个数据。因为数据升序排列,所以可以计算出下一个待处理位置的最大值(剩余数据/剩余的位置数)M,最小值是前一个数据m,在(m,M)中枚举每种情况。
代码
#include<iostream>
#include<vector>
using namespace std;
vector<int>a;
int N=0;
void dis(int n,int k){
if(k==1){
N++;
a.push_back(n);
vector<int>::iterator p=a.begin();
for(int j=0;j<a.size();j++){
if(j!=0)printf(" ");
printf("%d",*p);
p++;
}
printf("\n");
a.pop_back();
return;
}
int i=1;
if(!a.empty())i=a.back();
while(i*(k-1)<=n-i){
a.push_back(i);
dis(n-i,k-1);
a.pop_back();
i++;
}
return;
}
int main(){
int n,k;
scanf("%d %d",&n,&k);
dis(n,k);
printf("%d",N);
return 0;
}
7-3 数字变换
代码长度限制 16 KB
时间限制 100 ms
内存限制 5 MB
题目描述
利用变换规则,一个数可以变换成另一个数。变换规则如下:(1)x 变为x+1;(2)x 变为2x;(3)x 变为 x-1。给定两个数x 和 y,至少经过几步变换能让 x 变换成 y
输入格式
1行,2个整数x和y,用空格分隔, 1≤x,y≤100000.
输出格式输出格式
第1行,1个整数s,表示变换的最小步数。
第2行,s个数,用空格分隔,表示最少变换时每步变换的结果。规则使用优先级顺序: (1),(2),(3)。
输入样例
在这里给出一组输入。例如:
2 14
输出样例
在这里给出相应的输出。例如:
4
3 6 7 14
思路
广度优先遍历,x为起点,x+1,2x,x-1为x的三个儿子。用两个辅助数组,分别记录各结点的上一个结点(last)和该节点是否已被访问过。每次从队列取出一个元素,若他的儿子不超界且未被访问(因为先入队的路径一定更短),就入队。重复以上过程,直到遇见目标数(在元素入队前判断),就停止。通过last数组找到最短路径。
超界的情况:入队的元素可能会大于目标元素,这样只能靠x-1访问到目标元素,如果不加处理会浪费大量空间时间。我设定的界限为max=y+y-x+1;这个最大值足够小且能保证不合适的元素会被排除队列外。
代码
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<string.h>
using namespace std;
int max;
inline int next(int n,int i){
if(i==0)return n+1;
else if(i==1)return n*2;
else return n-1;
}
int*ifask;
int*last;
void bfs(int x,int y){
if(x==y)return;
ifask[x]=1;
queue<int>q;
q.push(x);
int now;
while(1){
now=q.front();
q.pop();
for(int i=0;i<3;i++){
int nxt=next(now,i);
if(nxt>0&&nxt<::max&&!ifask[nxt]){
ifask[nxt]=1;
last[nxt]=now;
if(nxt==y)return;
q.push(nxt);
}
}
}
}
int main(){
int x,y;
scanf("%d %d",&x,&y);
if(x>=y){
printf("%d\n",x-y);
if(x>y)printf("%d",x-1);
for(int i=x-2;i>=y;i--){
printf(" %d",i);
}
return 0;
}
::max=y+y-x+1;
ifask=new int[::max];
memset(ifask,0,::max*sizeof(int));
last=new int[::max];
bfs(x,y);//return 0;
stack<int>otpt;
int nstep=0;
while(y!=x){
otpt.push(y);
nstep++;
y=last[y];
}
if(!otpt.empty()){
printf("%d\n%d",nstep,otpt.top());
otpt.pop();
while(!otpt.empty()){
printf(" %d",otpt.top());
otpt.pop();
}
}else printf("%d\n%d",0,x);
return 0;
}
7-4 旅行 I
代码长度限制 16 KB
时间限制 1000 ms
内存限制 10 MB
题目描述
五一要到了,来一场说走就走的旅行吧。当然,要关注旅行费用。由于从事计算机专业,你很容易就收集到一些城市之间的交通方式及相关费用。将所有城市编号为1到n,你出发的城市编号是s。你想知道,到其它城市的最小费用分别是多少。如果可能,你想途中多旅行一些城市,在最小费用情况下,到各个城市的途中最多能经过多少城市。
输入格式
第1行,3个整数n、m、s,用空格分隔,分别表示城市数、交通方式总数、出发城市编号, 1≤s≤n≤10000, 1≤m≤100000 。
第2到m+1行,每行三个整数u、v和w,用空格分隔,表示城市u和城市v的一种双向交通方式费用为w , 1≤w≤10000。
输出格式
第1行,若干个整数Pi,用空格分隔,Pi表示s能到达的城市i的最小费用,1≤i≤n,按城市号递增顺序。
第2行,若干个整数Ci,Ci表示在最小费用情况下,s到城市i的最多经过的城市数,1≤i≤n,按城市号递增顺序。
输入样例
在这里给出一组输入。例如:
5 5 1
1 2 2
1 4 5
2 3 4
3 5 7
4 5 8
输出样例
在这里给出相应的输出。例如:
0 2 6 5 13
0 1 2 1 3
思路
经典的单源最短路问题,这里总结一下常见的几种方法
dijkstra
正权最短路问题最快算法(堆或者说优先队列优化)
从起始点开始,采用贪心算法的策略,每次遍历(松弛) 到始点距离最近且未访问过的顶点的 邻接节点(访问),直到扩展到终点为止
代码
我用的不是这种方法,下面展示的是别人的代码
struct pos
{
int dis, name;
pos(int a, int b) :dis(a), name(b) {}
bool operator<(const pos& b) const//运算符重载使优先队列递增
{
return dis > b.dis;
}
};
void dijkstra()
{
priority_queue<pos>q;
q.push(pos(0, s));
dis[s] = 0;
while (!q.empty())
{
pos cur = q.top();
q.pop();
int u = cur.name;
if (vis[u]) continue;
vis[u] = 1;
for (int v = head[u]; ~v; v = e[v].next)
{
int t = e[v].to;
if (dis[t] > e[v].w + dis[u])
{
dis[t] = e[v].w + dis[u];
num[t] = num[u] + 1;
q.push(pos(dis[t], t));
}
else if (dis[t] == e[v].w + dis[u])
{
if (num[u] + 1 > num[t])
{
num[t] = num[u] + 1;
}
}
}
}
}
spfa
设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾
关键代码
void dijs(int s){
queue<int>q;
dist[s]=0;
q.push(s);
while(!q.empty()){
int now=q.front();
q.pop();
vist[now]=0;//是否在队列中,初值为0
for(int s0=map[now];s0!=-1;s0=as[s0].next){
int to=as[s0].to;
if(dist[now]+as[s0].len<dist[to]){
dist[to]=dist[now]+as[s0].len;
nvia[to]=nvia[now]+1;
if(!vist[to]){
vist[to]=1;
q.push(to);
}
}
}
}
return;
}
完整代码
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
#define Mrl 0x7fffffff
struct side{
int to,len,next;
};
vector<side>as;
int*map;
void addSide(int from,int to,int len){
side s0;
s0.len=len;
s0.to=to;
s0.next=map[from];
as.push_back(s0);
map[from]=as.size()-1;
return;
}
int*dist;
int*vist;
int*nvia;
void dijs(int s){
queue<int>q;
dist[s]=0;
q.push(s);
while(!q.empty()){
int now=q.front();
q.pop();
vist[now]=0;
for(int s0=map[now];s0!=-1;s0=as[s0].next){
int to=as[s0].to;
if(dist[now]+as[s0].len<dist[to]){
dist[to]=dist[now]+as[s0].len;
nvia[to]=nvia[now]+1;
if(!vist[to]){
vist[to]=1;
q.push(to);
}
}else
if(dist[now]+as[s0].len==dist[to]){
if(nvia[now]+1>nvia[to]){
nvia[to]=nvia[now]+1;
q.push(to);
}
}
}
}
return;
}
int main(){
int n,m,s;
scanf("%d %d %d",&n,&m,&s);
{map=new int[n];
dist=new int[n];
vist=new int[n];
nvia=new int[n];}
for(int i=0;i<n;i++){
map[i]=-1;
dist[i]=Mrl;
vist[i]=0;
nvia[i]=0;
}
for(int i=0;i<m;i++){
int n1,n2,l;
scanf(" %d %d %d",&n1,&n2,&l);
n1--;n2--;
addSide(n1,n2,l);
addSide(n2,n1,l);
}
dijs(s-1);
for(int i=0;i<n;i++){
if(i!=0)printf(" ");
printf("%d",dist[i]);
}
printf("\n");
for(int i=0;i<n;i++){
if(i!=0)printf(" ");
printf("%d",nvia[i]);
}
return 0;
}

浙公网安备 33010602011771号