7-1 连通分量 (100 分)
无向图 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 n,m;
int father[100010];
int countt;
int Find(int v)
{
if(father[v]==v) return v;
return father[v]=Find(father[v]);
}
void Union(int x,int y)
{
father[Find(y)]=Find(x);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) father[i]=i;
int x,y;
for(int i=1;i<=m;i++)
{
cin>>x>>y;
Union(x,y);
}
for(int i=1;i<=n;i++)
{
if(father[i]==i) countt++;
}
printf("%d",countt);
return 0;
}
思路二:dfs/bfs搜索,遍历到的结点打标记,统计一共进行了多少次dfs/bfs.
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
typedef long long ll;
vector<int>vec[50001];
int n, m, bj[50002];
int countt;
void dfs(int x)
{
bj[x] = 1;
for (vector<int>::iterator it=vec[x].begin();it!=vec[x].end();it++)
{
if (!bj[*it]) dfs(*it);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= m; i++) //读入图
{
cin >> x >> y;
vec[x].push_back(y);
vec[y].push_back(x);
}
for (int j = 1; j <= n; j++)
{
if (!bj[j]) dfs(j),countt++;
}
printf("%d", countt);
return 0;
}
7-2 整数拆分 (100 分)
整数拆分是一个古老又有趣的问题。请给出将正整数 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
思路:dfs,每次加入一个数,保证其不低于上一个数且不超过剩余数的平均,最后达到要求的时候,最后一个数就是剩下的数。即在生成过程中保证了划分的数为递增的,且总和为N;
#include <iostream>
using namespace std;
int way[51];
int countt=0,k;
void dfs(int n,int s)
{
if(s==k)
{
way[s]=n; //确保正好分配
for(int i=1;i<s;i++)
{
cout << way[i] << " ";
}
cout << way[s]<<"\n";
countt++;
}
else
{
for(int i=way[s-1];i<=n/(k-s+1);i++) //确保递增
{
way[s]=i;
dfs(n-i,s+1);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >>n >> k;
way[0]=1; //保证第一次选取从1开始
dfs(n,1);
cout << countt;
return 0;
}
7-3 数字变换 (100 分)
利用变换规则,一个数可以变换成另一个数。变换规则如下:(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
思路:
开始的时候我用的是stl的队列,但数组就开到200001就超限。我只好改为手写队列(也挺好写的),开了一个father数组用来记录前驱(要求是最短路径,所以出现过的就不能重复记录),同时它还能起到标志数组的作用,因为0元素是不能入队的。最后还可以通过它来确定顺序和计算次数。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <stdlib.h>
using namespace std;
int x,y;
int countt;
int father[200011];
stack<int>endd;
int que[200011],f=0,r=0;
void bfs(int xx)
{
que[r++]=xx;
while(f<r)
{
int now=que[f];
f++;
if(now+1<=200010&&now+1>=1&&!father[now+1])
{
que[r++]=now+1;
father[now+1]=now;
if(now+1==y) return;
}
if(now*2<=200010&&now*2>=1&&!father[now*2]){
que[r++]=now*2;
father[now*2]=now;
if(now*2==y) return;
}
if(now-1<=200010&&now-1>=1&&!father[now-1]){
que[r++]=now-1;
father[now-1]=now;
if(now-1==y) return;
}
}
}
int main()
{
scanf("%d %d",&x,&y);
if(x==y) {cout<<0;exit(0);}
bfs(x);
int a=y;
while(a!=x)
{
countt++;
a=father[a];
endd.push(a);
}
printf("%d\n",countt);
endd.pop();
while(!endd.empty())
{
int now=endd.top();
endd.pop();
printf("%d ",now);
}
printf("%d",y);
}
7-4 旅行 I (100 分)
五一要到了,来一场说走就走的旅行吧。当然,要关注旅行费用。由于从事计算机专业,你很容易就收集到一些城市之间的交通方式及相关费用。将所有城市编号为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算法。最多城市我引入一个数组用来存储到达某一点经过的城市数,每次松弛操作的同时,一并更新最多城市。这里有一个问题就是当两条路到同一个点路径长度相等时,我们要选城市数多的赋值;当路径长度不等时,就不考虑城市数目。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
typedef long long ll;
struct node
{
int a;//bianhao
ll b;//cost
};
bool operator<(node aa, node bb)
{
return aa.b > bb.b;
}
const int INF = 1e9 + 5;
int n, m, u;
vector<pair<int, int> >vec1[10001];
priority_queue<node>pq1, pq2;
ll dist[10001];
int way[10001];
int bj1[10001];
void dijkstra()
{
bj1[u] = 1;
way[u]= 0;
for (int k = 1; k < n; k++)
{
for (vector<pair<int, int> >::iterator it = vec1[u].begin(); it != vec1[u].end(); it++)
{
int y = it->first;
ll w = it->second;
if (!bj1[y] && dist[u] + w < dist[y])
{
dist[y] = dist[u] + w;
way[y]=way[u]+1;
node sja;
sja.a = y;
sja.b = dist[y];
pq1.push(sja);
}
if (!bj1[y] && dist[u] + w == dist[y])
{
dist[y] = dist[u] + w;
way[y]=max(way[u]+1,way[y]);
node sja;
sja.a = y;
sja.b = dist[y];
pq1.push(sja);
}
}
node minn;
do
{
minn = pq1.top();
pq1.pop();
} while (bj1[minn.a]);
u = minn.a;
bj1[u] = 1;
}
}
int main()
{
cin.tie(0);
cin >> n >> m >> u;
int x, y, t;
for (int i = 1; i <= m; i++)
{
cin >> x >> y >> t;
vec1[x].push_back(make_pair(y, t));
vec1[y].push_back(make_pair(x, t));
}
for (int i = 1; i <= n; i++)
{
dist[i] = INF;
}
dist[u] = 0;
dijkstra();
for(int i=1;i<n;i++)
{
printf("%lld ",dist[i]);
}
printf("%lld\n",dist[n]);
for(int i=1;i<n;i++)
{
printf("%d ",way[i]);
}
printf("%d\n",way[n]);
}