填空题
4-1
求整数n(n≥0)的阶乘的算法代码如下,其时间复杂度是 O (n)。
int fact(int n){
if(n <= 1) return 1;
return n * fact(n - 1);
}
4-2
函数2logn 3 的渐进表达式是O(logn)。
4-3
函数1314+ 1/n 的渐进表达式是O(1)。
4-4
时间复杂度为O(nlogn)的排序算法有归并排序、堆排序和(快速)排序。
4-5
由权值分别为(3,8,6,2,5)的叶子结点生成一棵哈夫曼树,它的带权路径长度为(53)。
函数题
6-1 旅途加油
题目
一辆汽车加满油后可行驶nkm。旅途中有若干加油站。设计一个有效算法,对于给定的n和k个加油站位置,计算最少加油次数。
输入格式:
第1行输入两个整数n和k,分别表示表示汽车加满油后可行驶nkm,旅途中有k个加油站
第2行输入k+1个整数,表示第k个加油站与第k-1个加油站之间的距离。第0个加油站表示出发地,汽车已加满油。第k+1个加油站表示目的地。
输出格式:
输出最少加油次数。如果无法到达目的地,则输出“No Solution!”。
函数接口定义:
int Fill(int stations[],int n,int k);
其中, stations是用户传入的k+1个相邻加油站的距离,n是油箱满的时候可以走的距离,k是中间加油站的数量;函数须返回 最少加油次数,如果无法到达目的地,函数返回0。
裁判测试程序样例:
int Fill(int stations[],int n,int k);
int main(){
int stations[1000];
int n; //油箱满的时候可以走的距离
int k;//中间加油站的数量
int i,all;
scanf("%d%d",&n,&k);
for(i=0;i<k+1;i++)
scanf("%d",&stations[i]);
all=Fill(stations,n,k);
if(all!=0)
printf("%d\n",all);//输出总的加油次数
else
printf("No Solution!");
}
/* 请在这里填写答案 */
输入样例:
在这里给出一组输入。例如:
7 7
1 2 3 4 5 1 6 6
输出样例:
在这里给出相应的输出。例如:
4
答案:
//C语言
int Fill(int stations[],int n,int k){
//先用贪心算法做一遍
int ans=0;//用来记录已加油次数
int remain=n;//用来表示还剩余的油量
int flag=1;//表示最近一站是否加油,0代表没有,1代表有
for(int i=0;i<k+1;i++){
if(stations[i]<=remain){
//当目前油量足以到达下一站时,不加油
remain-=stations[i];
flag=0;
}else if(flag==0){
//当目前油量不足以达到下一站时,且该站还未加过油时
flag=1;
ans+=1;
remain=n;
i--;//加满油后重新判断
}else{
//当前站已经加过油,但还是不足以到达下一站
return 0;
}
}
return ans;
}
6-2杨辉三角-递归
题目
输入一个整数n(n<=10),输出n行构成的杨辉三角,要求设计递归函数实现。
函数接口定义:
int Yang(int i, int j);
参数i和j代表最终三角形的第i行第j列,函数的返回值则是杨辉三角第i行第j列上的正确数值。用递归实现。
裁判测试程序样例:
int Yang(int i, int j);
int main()
{
int n, i, j;
scanf("%d",&n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
printf("%4d", Yang(i,j));
}
printf("\n");
}
return 0;
}
/* 请在这里填写答案 */
输入样例:
一个正整数
4
输出样例:
每个数占4列
1
1 1
1 2 1
1 3 3 1
答案
int Yang(int i,int j){
//杨辉三角的特性 当j=1或i=j时,都为1
if(j==1||i==j){
return 1;
}
return Yang(i-1,j-1)+Yang(i-1,j);
}
6-3求解编辑距离问题(动态规划法)
题目
设A和B是两个字符串。现在要用最少的字符操作次数,将字符串A转换为字符串B。这里所说的字符操作共有3种:
(1)删除一个字符。
(2)插入一个字符。
(3)将一个字符替换另一个字符。
函数接口定义:
void solve();
裁判测试程序样例:
#include<stdio.h>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 201
//问题表示
string a;
string b;
//求解结果表示
int dp[MAX][MAX];
void solve(); //求dp
int main()
{
cin>>a>>b;
solve();
int i,j;
printf("%d",dp[a.length()][b.length()]);
return 0;
}
/* 请在这里填写答案 */
输入格式:
第一行输入A字符串,第二行输入B字符串。
输出格式:
输出最少的字符操作次数。
输入样例1:
sfdqxbw
gfdgw
输出样例1:
4
答案
//c++
//动态规划
//dp[i][j]表示将字符串a的前i个字符转化为字符串b的前j个字符所需的最少操作次数
void solve(){
//初始化
dp[0][0]=0;
dp[0][1]=1;
dp[1][0]=1;
for(int i=1;i<=a.length();i++){
for(int j=1;j<=b.length();j++){
if(a[i-1]==b[j-1]){
//如果字符相等,则dp保持
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;//如果字符不相等,则dp延申
}
}
}
}
6-4 电影安排
题目
终于到周末了,明明是特别喜欢看电影。他想在一天内尽量多的看到完整的多部电影。 现在他把他喜欢的电影的播放时间表给你,希望你能帮他合理安排。
输入格式:
输入的第一行是一个整数n(n<=100),表示明明喜欢的电影的总数。 接下来n行,每行输入两个整数si和ei(1<=i<=n),表示第i个电影的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。
输出格式:
输出能完整看到的电影的个数。
函数接口定义:
int Schedule(TIME a[],int n);//根据n部电影在一天的放映时间,返回至多能安排的电影场数
int cmp(TIME *a,TIME *b);//按结束时间升序排序的qsort排序规则,此处不表
快速排序库函数qsort原型为:void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) ); 例如:qsort(a,n,sizeof(TIME),cmp);表示将数组a前n个元素按照cmp排序规则进行排序
裁判测试程序样例:
#include<bits/stdc++.h>
using namespace std;
typedef struct
{
int start_time;
int end_time;
}TIME;
int Schedule(TIME a[],int n);//根据n部电影在一天的放映时间,返回至多能安排的电影场数
bool cmp(const TIME &a,const TIME &b);//按结束时间升序排序的qsort排序规则,此处不表
int main()
{
TIME a[100];
int n,i,k;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d%d",&a[i].start_time,&a[i].end_time);
k= Schedule(a,n);
printf("%d\n",k);
}
bool cmp(const TIME &a,const TIME &b) //按结束时间递增
{
return a.end_time<b.end_time;
}
/* 请在这里填写答案 */
输入样例:
第一行输入一个整数n,表示有n场电影,随后n行,每行输入一场电影的开始时间和结束时间,中间用空格分隔。
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
输出样例:
输出最多能看的电影场数
5
答案
//C++
//用贪心算法求解
int Schedule(TIME a[],int n){
sort(a,a+n,cmp);//先将数组按照结束时间升序排序
int count=0;//用来记录看电影场数
int myTime=0;//表示当前时间
for(int i=0;i<n;i++){
if(myTime<=a[i].start_time){
count++;
myTime=a[i].end_time;
}
}
return count;
}
6-5 循环日程安排问题(分治法)
题目
用分治法求解循环日程安排问题。设有n=2^k个选手要进行网球循环赛,要求设计一个满足以下要求的比赛日程表:
(1)每个选手必须与其他n-1个选手各赛一次。
(2)每个选手一天只能赛一次。
(3)循环赛在n-1天之内结束。
函数接口定义:
void Plan(int a[][N],int k);
裁判测试程序样例:
#include <iostream>
#include <math.h>
#include <iomanip>
using namespace std;
#define N 100
void Plan(int a[][N],int k);
int main()
{
int i,j,a[N][N],k,size;
cin>>k;
size=pow(2,k);
Plan(a,k);
for(i=1;i<=size;i++){
for(j=1;j<=size;j++)
{
cout<<a[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
/* 请在这里填写答案 */
输入样例:
输入K值。
3
输出样例:
输出比赛日程表。
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
答案
//C++
//分治法 1分治 2递归 3合并
void Plan(int a[][N],int k){
//递归出口
if(k==1){
a[1][1]=1;a[1][2]=2;
a[2][1]=2;a[2][2]=1;
}else{
Plan(a,k-1);
//将新行格子赋值
int start=pow(2,k-1)+1;
int end=pow(2,k);
int t=end-start+1;
for(int i=start;i<=end;i++){
for(int j=1;j<=t;j++){
a[i][j]=a[i-t][j]+t;
}
}
//将对角线赋值
for(int i=1;i<=end;i++){
for(int j=start;j<=end;j++){
if(i+t>end){
//交换左上右下
a[i][j]=a[(i+t)%end][j-t];
}else{
//交换左下右上
a[i][j]=a[i+t][j-t];
}
}
}
}
}
6-6 归并排序
题目
归并排序算法,实现其中的mergeSort和merge两个函数。
函数接口定义:
void mergeSort(vector<int> &Array, int start, int end);
void merge(vector<int> &Array, int start, int mid, int end);
函数mergeSort完成归并排序(从小到大),其中 Array 是需要排序的数组,start是Array 中排序部分的起始位置,end是Array 中排序部分的结束位置。
函数merge完成数组中两个相邻有序部分(从小到大)的合并,合并后元素仍然有序(从小到大),其中 Array 是需要排序的数组,start是第一个有序部分的起始位置,mid是第一个有序部分的结束位置,第二个有序部分的起始位置为mid+1,end是第二个有序部分的结束位置。
裁判测试程序样例:
#include <iostream>
#include <vector>
using namespace std;
void mergeSort(vector<int> &Array, int start, int end);
void merge(vector<int> &Array, int start, int mid, int end);
int main(){
int n;
vector<int> a;
vector<int>::iterator i;
//输入数组
cin >> n;
a.resize(n);
for(i = a.begin(); i != a.end(); i++){
cin >> *i;
}
//归并排序
mergeSort(a, 0, a.size()-1);
//输出数组
i = a.begin();
cout << *i++;
for( ; i != a.end(); i++){
cout << " " << *i;
}
return 0;
}
/* 此处将 放置 提交的函数代码 */
输入样例:
第一行为数组元素个数
第二行为空格间隔的各个数组元素
5
3 1 2 5 4
输出样例:
输出排序后的数组元素
1 2 3 4 5
答案
//归并排序
void mergeSort(vector<int> &Array,int start,int end){
//递归终止条件
if(end<=start){
return;
}
//分治
int mid=(start+end)/2;
mergeSort(Array,start,mid);
mergeSort(Array,mid+1,end);
//合并
merge(Array,start,mid,end);
}
//合并
void merge(vector<int> &Array,int start,int mid,int end){
int temp[end-start+1];
int i=start,j=mid+1,k=0;
while(i<=mid&&j<=end){
if(Array[i]<=Array[j]){
temp[k]=Array[i];
i++;
}else{
temp[k]=Array[j];
j++;
}
k++;
}
while(i<=mid){
temp[k]=Array[i];
k++;i++;
}
while(j<=end){
temp[k]=Array[j];
k++;j++;
}
k=0;
for(i=start;i<=end;i++,k++){
Array[i]=temp[k];
}
}
6-7 哈夫曼树及编码
题目
构造哈夫曼树,计算WPL。
函数接口定义:
void CreateHTree();
void CreateHCode();
裁判测试程序样例:
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <map>
using namespace std;
#define MAX 101
int n;
struct HTreeNode //哈夫曼树结点类型
{
char data; //字符
int weight; //权值
int parent; //双亲的位置
int lchild; //左孩子的位置
int rchild; //右孩子的位置
};
HTreeNode ht[MAX]; //哈夫曼树
map<char,string> htcode; //哈夫曼编码
struct NodeType //优先队列结点类型
{
int no; //对应哈夫曼树ht中的位置
char data; //字符
int weight; //权值
bool operator<(const NodeType &s) const
{
//用于创建小根堆
return s.weight<weight;
}
};
void CreateHTree();
void CreateHCode();
int WPL() //求WPL
{
int wps=0;
for (int i=0;i<n;i++)
wps+=ht[i].weight*htcode[ht[i].data].size();
return wps;
}
int main()
{
cin >> n;
for(int i=0;i<n;i++)
{
cin >> ht[i].data >> ht[i].weight;
}
CreateHTree(); //建立哈夫曼树
CreateHCode(); //求哈夫曼编码
printf("WPL=%d",WPL());
return 0;
}
/* 请在这里填写答案 */
输入样例:
第一行输入一个数n(1<n<100),表示叶子节点的个数,接下去输入n行,每行输入一个字符和一个整数,表示每个节点表示的字符和权值。
5
A 8
B 10
C 2
D 11
E 1
输出样例:
输出WPL。
WPL=67
答案
//c++
//构造哈夫曼树
void CreateHTree(){
//先按照权值排序
priority_queue<NodeType> pq;
for(int i=0;i<n;i++){
//装载进优先队列
NodeType node;
node.no=i;
node.data=ht[i].data;
node.weight=ht[i].weight;
pq.push(node);
//给ht数组赋初值
ht[i].parent=-1;
ht[i].lchild=-1;
ht[i].rchild=-1;
}
//构造哈夫曼树
int pos=n;
//新增n-1个中间节点
for(int i=0;i<n-1;i++){
//构造新中间节点
NodeType left=pq.top();pq.pop();
NodeType right=pq.top();pq.pop();
ht[pos].weight=left.weight+right.weight;
ht[pos].lchild=left.no;
ht[pos].rchild=right.no;
ht[pos].data='#';
ht[pos].parent=-1;
ht[left.no].parent=pos;
ht[right.no].parent=pos;
//加入优先队列
NodeType node;
node.no=pos;
node.data='#';
node.weight=ht[pos].weight;
pq.push(node);
pos++;
}
}
//求哈夫曼编码
void CreateHCode(){
for(int i=0;i<n;i++){
string code="";
int temp=i;
while(ht[temp].parent!=-1){
if(ht[ht[temp].parent].lchild==temp){
code+="0";
}else{
code+="1";
}
temp=ht[temp].parent;
}
htcode[ht[i].data]=code;
}
}
6-8 求解图的m着色问题(回溯法)
题目
给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的两个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。
函数接口定义:
void dfs(int i);
裁判测试程序样例:
#include <stdio.h>
#include <string.h>
#define MAXN 20 //图最多的顶点个数
int n,k,m;
int a[MAXN][MAXN];
int count=0; //全局变量,累计解个数
int x[MAXN]; //全局变量,x[i]表示顶点i的着色
bool Same(int i) //判断顶点i是否与相邻顶点存在相同的着色
{
for (int j=1;j<=n;j++)
if (a[i][j]==1 && x[i]==x[j])
return false;
return true;
}
int main()
{
memset(a,0,sizeof(a)); //a初始化
memset(x,0,sizeof(x)); //x初始化
int x,y;
scanf("%d%d%d",&n,&k,&m); //输入n,k,m
for (int j=1;j<=k;j++)
{
scanf("%d%d",&x,&y); //输入一条边的两个顶点
a[x][y]=1; //无向图的边对称
a[y][x]=1;
}
dfs(1); //从顶点1开始搜索
if (count>0) //输出结果
printf("%d",count);
else
printf("-1");
return 0;
}
/* 请在这里填写答案 */
输出格式:
程序运行结束时,将计算出的不同的着色方案数输出。如果不能着色,程序输出-1。
输入样例:
5 8 4
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
输出样例:
48
答案
//c++
//求解(i代表顶点)
void dfs(int i){
//终止条件(收集答案)
if(i>n){
count++;
return;
}
//遍历尝试着色
for(int color=1;color<=m;color++){
x[i]=color;
if(Same(i)){
dfs(i+1);
}
//回溯
x[i]=0;
}
}
6-9 最短路径(队列式分枝限界法)
题目
试实现最短路径算法。
函数接口定义:
void bfs();
void dispallpath();
裁判测试程序样例:
#include <stdio.h>
#include <string.h>
#include <queue>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f //表示∞
#define MAXN 51
int n,m; //图顶点个数和边数
int a[MAXN][MAXN]; //图的邻接矩阵
int v; //源点
int dist[MAXN]; //dist[i]源点到顶点i的最短路径长度
struct NodeType //队列结点类型
{
int vno; //顶点编号
int length; //路径长度
};
void bfs();
void dispallpath();
int main()
{
memset(dist,INF,sizeof(dist)); //初始化为∞
memset(a,INF,sizeof(a)); //初始化为∞
cin >> n>> m;
for (int i=0;i<n;i++) //对角线设置为0
a[i][i]=0;
for(int i=0;i<m;i++){
int x,y,w;
cin>>x>>y>>w;
a[x][y]=w;
}
cin >>v;
bfs();
dispallpath();
return 0;
}
/* 请在这里填写答案 */
输入样例:
第1行输入结点数vexnum和边数arcnum。接下来依次输入arcnum行,每行输入3个值,前两个字符表示结点编号,后一个数表示两个结点之间边的权值。最后一行输入源点编号。
6 8
0 5 100
0 2 10
0 4 30
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
0
输出样例:
输出源点到终点(按终点编号递增顺序)的最短路径,无路径输出0。
0
10
50
30
60
答案
//c++
//单源最短路径(Dijistra算法)
void bfs(){
int visit[MAXN];
memset(visit,0,sizeof(visit));
visit[v]=1;//标记源点为已访问
dist[v]=0;//更新源点到源点距离为0
int temp=v;//指向刚访问的顶点
//循环n-1次,用n-1个顶点更新剩下的距离
for(int i=0;i<n-1;i++){
//每次更新,都遍历所有没有访问过的顶点
int minDist=INF;//记录最小距离
int minN=-1;
for(int j=0;j<n;j++){
if(visit[j]==1){
continue;
}
if(dist[temp]+a[temp][j]<dist[j]){
dist[j]=dist[temp]+a[temp][j];
}
if(dist[j]<minDist){
minDist=dist[j];
minN=j;
}
}
//更新加入结点
visit[minN]=1;
temp=minN;
}
}
//打印源点到各个顶点的距离
void dispallpath(){
for(int i=0;i<n;i++){
if(i==v){
continue;
}
if(dist[i]!=INF){
cout<<dist[i]<<endl;
}else{
cout<<0<<endl;
}
}
}
编程题
7-1 第k小元素
题目
给定一个大小为n(1≤n≤1000000)且无序的整型数组,数组中可能存在相同元素,请找出该数组第k(1≤k≤n)小的元素,注意这里的第k小元素指的是按从小到大排序后的第k个位置上的元素。
输入格式:
每个输入文件为一个测试用例,每个文件的第一行给出两个正整数n和k,第二行给出n个整数,其间以空格分隔。
输出格式:
输出第k小元素的值。
输入样例:
10 4
2 3 5 12 4 9 3 8 2 9
输出样例:
3
答案
//c++
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;//数组长度
int k;//求解第k小
vector<int> v;//存储无序整数数组
bool cmp(int x,int y){
return x<y;
}
int main(){
cin>>n>>k;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
v.push_back(temp);
}
sort(v.begin(),v.end(),cmp);
cout<<v[k-1]<<endl;
return 0;
}
7-2 二分搜索
题目
输入样例:
第一行输入一个数n,第二行输入n个数,第三行输入要查的值。
12
11 14 23 25 68 78 84 97 155 201 310 479
97
输出样例:
输出key在序列中的位置。
7
答案
//c++
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n;//数字个数
int m;//要查询的值
vector<int> v;
int myFind(int start,int end){
int mid=(start+end)/2;
if(v[mid]==m){
return mid;
}else if(v[mid]<m){
return myFind(mid+1,end);
}else{
return myFind(start,mid-1);
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
v.push_back(temp);
}
cin>>m;
//调用库函数查找
// auto it=find(v.begin(),v.end(),m);
// if(it!=v.end()){
// cout<<distance(v.begin(),it);
// }else{
// cout<<0;
// }
//二分查找实现
cout<<myFind(0,n-1);
return 0;
}
7-3 快速幂运算
题目
编程求x^y 最后三位数表示的整数
输入格式:
输入在一行中给出两个整数x和y,其中1<=x,y<=1000000000
输出格式:
输出占一行,是x^y 的最后三位表示的整数。
输入样例1:
在这里给出一组输入。例如:
2 3
输出样例1:
在这里给出相应的输出。例如:
8
输入样例2:
在这里给出一组输入。例如:
12 6
输出样例2:
在这里给出相应的输出。例如:
984
答案
//c++
//直接用pow做只能拿部分分,因为快速幂范围很大
//由于结果只求最后三位,故可以用二分法求解,部分积只保留最后三位,保证不溢出
#include<iostream>
using namespace std;
long x,y;
int quickPow(int a,int b){
if(b==1){
return a%1000;
}else{
int r=quickPow(a,b/2);
if(b%2==0){
return r*r%1000;
}else{
return r*r*a%1000;
}
}
}
int main(){
cin>>x>>y;
int result=quickPow(x,y);
cout<<result<<endl;
return 0;
}
7-4 h0154.加勒比海盗船——最优装载问题
题目
有一天,海盗们截获了一艘装满各种各样古董的 货船,每一件古董都价值连城,一旦打碎就失去了它 的价值。虽然海盗船足够大,但载重量为 C,每件古 董的重量为 wi,海盗们该如何把尽可能多数量的宝贝 装上海盗船呢?
输入格式:
第1行输入T组测试数据,每组测试数据输入载重量 c 及古董个数 n,下1行输入每个古董的重量wi,用空格分开.
输出格式:
每组能装入的古董最大数量
输入样例:
1
30 8
4 10 7 11 3 5 14 2
输出样例:
5
答案
//c++
//贪心算法
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int T;//测试数据数量
int c,n;
bool cmp(int x,int y){
return x<y;
}
int main(){
cin>>T;
while(T--){
cin>>c>>n;
int count=0;
int sum=0;
vector<int> v;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
v.push_back(temp);
}
sort(v.begin(),v.end(),cmp);
for(int i=0;i<n;i++){
sum+=v[i];
if(sum<=c){
count++;
}else{
break;
}
}
cout<<count<<endl;
}
return 0;
}
7-5 最小生成树-prim
题目
题目给出一个无向连通图,要求求出其最小生成树的权值。
温馨提示:本题请使用prim最小生成树算法。
输入格式:
第一行包含两个整数 N(1<=N<=1x10^4 ),M(1<=M<=2x10 ^6 ) 表示该图共有 N 个结点和 M 条无向边。
接下来 M 行每行包含三个整数 Xi ,Y i ,Z i ,表示有一条长度为 Z i 的无向边连接结点 X i ,Y i 。
输出格式:
输出一个整数表示最小生成树的各边的长度之和。
输入样例:
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出样例:
7
答案
//c++
#include<bits/stdc++.h>
using namespace std;
#define maxx 10000010//这里需要7位数
//定一结构体数组来存每组数据
struct Node{
int from;
int to;
int val;
}node[maxx];
int father[maxx];
bool sort_val(Node a,Node b){
return a.val < b.val;
}
//查询元素的根节点
int find( int a ){
int r=a;
while(father[r]!=r)
r=father[r]; //找到他的前导结点
int i=a,j;
while(i!=r){
//路径压缩算法
j=father[i]; //记录x的前导结点
father[i]=r; //将i的前导结点设置为r根节点
i=j;
}
return r;
}
//合并根节点不同的联通分量
void merg(int a,int b){
// int a = find(x);//查询x的根节点
// int b = find(y);//查询y的根节点
// if(a != b){
father[a] = b;
// }
}
int main(){
int n,m;
int sum = 0;
//cin >> n >> m;
scanf("%d%d",&n,&m);
//初始化father数组 将其每个顶点的根节点设置为自己的节点号
for(int i = 1; i <= n; i++){
father[i] = i;
}
for(int i = 0; i < m; i++){
//cin >> node[i].from >> node[i].to >> node[i].val;
scanf("%d%d%d",&node[i].from,&node[i].to,&node[i].val);
}
sort(node,node+m,sort_val);
// for(int i = 0; i < m; i++){
// cout << node[i].val << endl;
// }
int count = 0;
for(int i = 0; i < m; i++){
if(count == n - 1){
//n个顶点需要 n - 1边
break;
}
int a = find(node[i].from);
int b = find(node[i].to);
if(a != b){
father[a] = b;
sum += node[i].val;
count++;
}
}
printf("%d",sum);
// cout << sum;
}
7-6 最大子段和问题
题目
最大子段和问题。给定由n个整数组成的序列,求序列中子段的最大和,若所有整数均为负整数时定义最大子段和为0。
输入格式:
第一行输入整数个数n(1≤n≤10000),再依次输入n个整数。
输出格式:
输出第一行为最大子段和,第二行为子段第一个数和最后一个数在整个序列中的位序。
输入样例1:
5
-2 11 -4 13 -5 -2
输出样例1:
20
2 4
答案
//c++
#include<iostream>
#include<vector>
using namespace std;
#define MAX 10000
int n;
vector<int> v;
int result=0;
int resStart=0,resEnd=0;
int start=0,myend=0;//存储临时开始和结束
int dp[MAX]={
0};//dp[i]表示前i个子段的最大值
void solve(){
for(int i=0;i<n;i++){
if(dp[i]>0){
if(dp[i]+v[i]<0){
dp[i+1]=0;
start=i;
myend=i;
}else{
dp[i+1]=dp[i]+v[i];
myend=i;
}
}else{
start=i;
myend=i;
dp[i+1]=max(v[i],0);
}
if(dp[i+1]>result){
resStart=start;
resEnd=myend;
result=dp[i+1];
}
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
v.push_back(temp);
}
solve();
cout<<result<<endl;
cout<<resStart+1<<" "<<resEnd+1<<endl;
return 0;
}
7-7 最长子序列
题目
给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。
输入格式:
第一行包含整数n,1≤n≤1000。
第二行n个整数,表示完整序列,数值范围是[-109,109]。
输出格式:
输出一个整数,表示数值严格单调递增的最长子序列的长度。
输入样例:
7
3 1 2 1 8 5 6
输出样例:
4
答案
//c++
#include<iostream>
#include<vector>
using namespace std;
#define MAX 1000
#define INF 0x3f3f3f3f
int n;//数列长度
vector<int> v;//存储数列
int dp1[MAX]={
0};//dp1[i]表示前i个序列中,严格单调递增的最长子序列长度;
//dp2[i]表示前i个序列中,序列末尾元素值
int dp2[MAX]={
0};
void solve(){
dp1[0]=0;
dp2[0]=-INF;
dp1[1]=1;//前1个元素肯定满足单调递增
dp2[1]=v[0];//存第一个元素的值
for(int i=1;i<n;i++){
//从第二个元素开始遍历
int maxL=0;
int maxS=-INF;
//遍历0~i的dp,求解dp1[i+1]和dp2[i+1];
for(int j=0;j<=i;j++){
if(v[i]>dp2[j]){
if(maxL<dp1[j]+1){
maxL=dp1[j]+1;
maxS=v[i];
}
}
}
dp1[i+1]=maxL;
dp2[i+1]=maxS;
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
v.push_back(temp);
}
solve();
cout<<dp1[n]<<endl;
return 0;
}
7-8 N皇后问题
题目
在N×N格的国际象棋盘上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,对于给定的N,求出有多少种合法的放置方法。
输入格式:
一个正整数N≤10,表示棋盘和皇后的数量;
输出格式:
一个正整数,表示对应输入行的皇后的不同放置方法。
输入样例:
在这里给出一组输入。例如:
8
输出样例:
在这里给出相应的输出。例如:
92
答案
//c++
//回溯法
#include<iostream>
#include<vector>
using namespace std;
#define MAX 10
int n;
int arr[MAX][MAX]={
0};
int result=0;
struct Dir{
int x;
int y;
};
Dir dir[4];
//判断是否越界
bool inBorder(int x,int y){
if(x<0||x>=n||y<0||y>=n){
return false;
}
return true;
}
//判断是否冲突
bool safe(int x,int y){
//判断行冲突
for(int j=0;j<n;j++){
if(j==y) continue;
if(arr[x][j]==1) return false;
}
//判断列冲突
for(int i=0;i<n;i++){
if(i==x) continue;
if(arr[i][y]==1) return false;
}
//判断斜线冲突(4个方向)
for(int k=0;k<4;k++){
int tempx=x+dir[k].x,tempy=y+dir[k].y;
while(inBorder(tempx,tempy)){
if(arr[tempx][tempy]==1){
return false;
}
tempx+=dir[k].x;tempy+=dir[k].y;
}
}
return true;
}
//表示摆放第count个皇后
void dfs(int count){
//收集答案
if(count>n){
result++;
return;
}
//第count个皇后只摆放在第count行
int x=count-1;
for(int y=0;y<n;y++){
if(safe(x,y)){
arr[x][y]=1;
dfs(count+1);
arr[x][y]=0;
}
}
}
int main(){
dir[0].x=-1;dir[0].y=1; //右上
dir[1].x=1;dir[1].y=1; //右下
dir[2].x=-1;dir[2].y=-1; //左上
dir[3].x=1;dir[3].y=-1; //左下
cin>>n;
dfs(1);
cout<<result<<endl;
return 0;
}
7-9 走迷宫
题目
在一个 m×n 的迷宫里,从起点开始,依次按东(右)、南(下)、西(左)、北(上) 4 个方向探索通路,直至达到终点为止。
迷宫由字符组成,W 表示墙,. 表示空地,请编写程序,输出你找到的首条通道。
输入格式
迷宫的行数 m 和列数 n (0<m,n≤100)
m 行 n 列字符
起点的行号(0 ~ m - 1)和列号(0 ~ n - 1)
终点的行号(0 ~ m - 1)和列号(0 ~ n - 1)
输出格式
若问题无解,则输出 None
若问题有解,则输出迷宫:
W 表示墙
. 表示未走过的空地
o 表示走不通而退回时经过的空地
* 表示通道经过的空地
输入样例1
5 7
W W . W W W W
W . . . W . .
W . W W W . W
W . W . . . W
W W W . W W W
0 2
4 3
输出样例1
None
输入样例2
5 7
W W . W W W W
W . . . W . .
W . W W W . W
W . . . W . W
W W W . W W W
0 2
4 3
输出样例2
W W * W W W W
W * * o W . .
W * W W W . W
W * * * W . W
W W W * W W W
答案
//c++
#include<bits/stdc++.h>
using namespace std;
int m, n;
int start_x, start_y;
int end_x, end_y;
char room[105][105];
int book[105][105];
int flag=0;
void dfs(int x, int y) {
int next[4][2] = {
{
0,1},{
1,0},{
0,-1},{
-1,0}
};
if (x == end_x && y == end_y&&flag==0) {
for (int i = 0;i < m;i++) {
for (int j = 0;j < n;j++) {
if (j != n - 1)
cout << room[i][j] << " ";
else
cout << room[i][j] << endl;
}
}
flag=1;
return;
}
int tx=x, ty=y;
for (int i = 0;i < 4;i++) {
tx = x + next[i][0];
ty = y + next[i][1];
if (tx < 0 || ty < 0 || tx >= m || ty >= n)
continue;
if (book[tx][ty] == 0 && room[tx][ty] == '.') {
book[tx][ty] = 1;
room[tx][ty] = '*';
dfs(tx, ty);
room[tx][ty] = 'o';
book[tx][ty] = 0;
}
}
}
int main() {
cin >> m >> n;
for (int i = 0;i < m;i++) {
for (int j = 0;j < n;j++)
cin >> room[i][j];
}
cin >> start_x >> start_y >> end_x >> end_y;
// book[start_x][start_y] = 1;
// room[start_x][start_y] = '*';
dfs(start_x, start_y-1);
if(flag==0)
cout<<"None"<<endl;
}
7-10 背包问题-01背包
题目
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式:
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式:
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例:
4 5
1 2
2 4
3 4
4 5
输出样例:
8
答案
//c++
#include<bits/stdc++.h>
using namespace std;
#define MAX 1000
int N,M;
int v[MAX]={
0};
int w[MAX]={
0};
int dp[MAX]={
0};//dp[i]表示容量为i时,背包所能装的最大价值
int main(){
cin>>N>>M;
for(int i=0;i<N;i++){
cin>>v[i]>>w[i];
}
//01背包
for(int i=0;i<N;i++){
//当前物品
for(int j=M;j>=0;j--){
//当前背包容量 倒推
if(j>=v[i]){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
}
cout<<dp[M]<<endl;
return 0;
}