文章目录
简介
这里包含了厦大信院小学期校外实训各个算法的代表题目,如果你不想把所有的题全做一遍,请着重做这十几道题.学弟学妹们,光荣在于平淡,艰巨在于漫长,共勉!
[双指针]三数之和
题目
数据结构
将数据读入vector Nums容器中,然后按升序排序,设置vector<vector> res容器来存储结果,每个元素是一组ijk
算法
设置i,j,k三个指针,i每次循环1都更新k到n-1,同时j设置为i+1,进行循环2,然后循环3在ijk三者指向的对象大于target的时候k–,小于target的时候停止循环,如果等于的时候则将i,j,k作为一个vector存入res
代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<vector<int> > threesum(vector<int>& Nums,int target)
{
sort(Nums.begin(),Nums.end());//排序,使数列具有有序性,可以使用双指针
vector<vector<int> > res;
for(int i=0;i<Nums.size();i++)
{
if(i!=0&&Nums[i]==Nums[i-1])continue;//在不影响原来数组的情况下跳过了重复元素
k=Nums.size()-1;
for(int j=i+1;j<k;j++)
{
if(j>i+1&&Nums[j]==Nums[j-1])continue;
while(j<k&&Nums[i]+Nums[j]+Nums[k]>=target)//小于的时候跳出循环
{
//等于的时候
if(Nums[i]+Nums[j]+Nums[k]==target)
{
res.push_back(vector<int>(1, Nums[i]));//用了临时对象,1代表这个对象容纳一个元素
res.back().push_back(Nums[j]);
res.back().push_back(Nums[k]);
break;
}
//大于的时候
k--;
}
}
}
return res;
}
bool compare(vector<int> v1,vector<int> v2)
{
if(v1[0]<v2[0])
return true;
else if(v1[0]==v2[0]&&v1[1]<v2[1])
return true;
else return false;
}
int main(void)
{
int target,n;
vector<int> Nums;
cin>>target>>n;
int tem;
for(int i=0;i<n;i++)
{
cin>>tem;
Nums.push_back(tem);
}
vector<vector<int> > res=threesum(Nums,target);
sort(res.begin(),res.end(),compare);
for(auto it:res)
cout<<it[0]<<" "<<it[1]<<" "<<it[2]<<endl;
}
[深度优先搜索]八皇后
题目
数据结构
用res这个二维数组来存储解法,用temp数组来存储每次产生的解法,用col,dg,udg来担任列的,斜线的,反斜线的状态数组,用全局变量count来记录现在到了哪个解法,layer来表示循环到了哪层
算法
深搜算法,layer表示循环层数,也表示某一行,循环八个列,如果某个位置列,斜线,反斜线都不冲突,就将解法记录下来并且改变状态,进入下一个循环,然后将状态改变回去.
代码
#include<iostream>
using namespace std;
const int N=8;
int res[100][N];
int temp[N];
bool col[N],dg[2*N],udg[2*N];//dg是正斜线
int count=1;
void dfs(int layer)
{
if(layer==N)
{
for(int i=0;i<N;i++)
{
res[count][i]=temp[i];
}
count++;
}
for(int i=1;i<=N;i++)
{
if(!col[i]&&!dg[N-1-i+layer]&&!udg[layer+i])
{
col[i]=dg[N-1-i+layer]=udg[layer+i]=1;
temp[layer]=i;
dfs(layer+1);
col[i]=dg[N-1-i+layer]=udg[layer+i]=0;
}
}
}
int main(void)
{
dfs(0);
int t;
cin>>t;
while(t--)
{
int k;
cin>>k;
for(int i=0;i<N;i++)
cout<<res[k][i];
cout<<endl;
}
}
[递归]24点
题目
数据结构
递归的每一层都用一个大小递减的数组来存储未参与运算的数字和两个数字运算的结果
算法
随意挑选两个数进行运算(一共有六种情况)后,剩下的数字构成相同的问题,可以形成递归
代码
#include<iostream>
using namespace std;
bool isequal(double a)
{
return abs(a-24)<=1e-9;
}
bool count24(double a[],int n)
{
if(n==1)
return isequal(a[0]);
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
double temp[n-1];
int itemp=0;
for(int k=0;k<n;k++)
{
if(k!=j&&k!=i)
temp[itemp++]=a[k];
}
temp[n-2]=a[i]+a[j];
if(count24(temp,n-1))return true;
temp[n-2]=a[i]-a[j];
if(count24(temp,n-1))return true;
temp[n-2]=a[j]-a[i];
if(count24(temp,n-1))return true;
temp[n-2]=a[i]*a[j];
if(count24(temp,n-1))return true;
if(a[j]!=0)
{
temp[n-2]=a[i]/a[j];
if(count24(temp,n-1))return true;
}
if(a[i]!=0)
{
temp[n-2]=a[j]/a[i];
if(count24(temp,n-1))return true;
}
}
}
return false;
}
int main(void)
{
double inputnums[4];
while(1)
{
for(int i=0;i<4;i++)
{
cin>>inputnums[i];
}
if(inputnums[0]==0)
break;
if(count24(inputnums,4))
cout<<"Yes";
else cout<<"NO";
}
}
[深度优先搜索][状态变换]熄灯问题
题目
数据结构
用bitset格式的数组lights,res来存储灯和熄灭顺序,用temp_lights来复制灯用于不同的第一行熄灭顺序,open来存储每一行的熄灭顺序
算法
第一行有6个格,从000000到1111111正好对应十进制的0到63,所以循环六十三次,用k循环一行中的每一列,如果是1,那么进行相应的改变.用异或代表这一行的解决方案对下一行的影响,然后用这一行没有解决的灯作为下一行的解决方案
代码
#include<iostream>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;
bitset<6> lights[5],temp_lights[5],res[5],open;
void lightsoff()
{
for(int i=0;i<64;i++)
{
for(int q=0;q<5;q++)temp_lights[q]=lights[q];
open=i;
for(int j=0;j<5;j++)
{
for(int k=0;k<6;k++)
{
if(open.test(k))
{
temp_lights[j].flip(k);
if(k!=0)//如果是最左边,那么左边的不熄灭
temp_lights[j].flip(k-1);
if(k!=5)//如果是最右边,那么右边的不熄灭
temp_lights[j].flip(k+1);
}
}
if(j<4) temp_lights[j+1]^=open;
res[j]=open;
open=temp_lights[j];
}
if(temp_lights[4].none())
{
for(int i=0;i<5;i++)
{
for(int j=0;j<6;j++)
{
cout<<res[i][j]<<" ";
}
cout<<endl;
}
break;
}
}
}
int main(void)
{
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
cout<<"PUZZLE #"<<i<<endl;
for(int i=0;i<5;i++)
{
for(int j=0;j<6;j++)
{
int t;
cin>>t;
lights[i].set(j,t);
}
}
lightsoff();
}
}
[快速排序]大数排序
算法
从上到下递归,先选定一个数,比他大的都放到他的左边,否则就放右边,然后利用分治一层层下去
代码
#include<iostream>
using namespace std;
void quicksort(int array[],int l,int r)
{
if(l>=r)
return;
int i=l-1,j=r+1,x=array[l];
while(i<j)
{
do i++;while(array[i]<x);
do j--;while(array[j]>x);
if(i<j)swap(array[i],array[j]);
}
quicksort(array,l,j);
quicksort(array,j+1,r);
}
int main(void)
{
int array[100010];
int t;
scanf("%d",&t);
for(int i=0;i<t;i++)
scanf("%d",&array[i]);
quicksort(array,0,t-1);
for(int i=0;i<t;i++)
cout<<array[i]<<" ";
}
//一些注意事项请看https://www.acwing.com/solution/content/16777/
[归并排序]逆序对个数
题目
算法
从下向上递归,选定所排序部分的中间,一分为二,先递归将两部分分别排好,每次选择比较小的放入临时数组,完成对此部分排序.
代码
#include<iostream>
using namespace std;
int tmp[100010],q[100010];
long long merge_sort(int q[],int l,int r)
{
if(l>=r)
return 0;
int mid=(l+r)>>1;
long long res=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);
int k=0,i=l,j=mid+1;//注意这里是mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<=q[j])
tmp[k++]=q[i++];
else
{
res+=mid-i+1;
tmp[k++]=q[j++];
}
}
while(i<=mid)
tmp[k++]=q[i++];
while(j<=r)
tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++)
q[i]=tmp[j];
return res;
}
int main(void)
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>q[i];
long long res=merge_sort(q,0,n-1);
cout<<res;
}
#include
#include
#include
using namespace std;
bool chessboard[30][30];
int step,p,q;
vector<pair<char,int> > path;
int dy[8]={-2,-2,-1,-1,1,1,2,2},dx[8]={-1,1,-2,2,-2,2,-1,1};
bool dfs(int a,int b,int step)
{
chessboard[a][b]=1;
path.push_back(make_pair(‘A’+b,a+1));
if(step==p*q)
return true;
for(int i=0;i<8;i++)
{
int x=dx[i]+a,y=dy[i]+b;
if(x>=0&&x<p&&y>=0&&y<q&&chessboard[x][y]==0)
{
if(dfs(x,y,step+1))
return true;
}
}
chessboard[a][b]=0;
path.pop_back();
return false;
}
[洪水填充]骑士林克的怜悯
题目
数据结构
bool型数组 chessboard用于表示是否走过,step用于计算走过的步数,path用来存储走过的路径
算法
每一组数据都要重制step chessboard,清空path,循环每个位置作为出发点
内部以字母优先,数字在后的顺序循环,保证了每次都是字典序
由于第一组数据,所以造成影响和回溯都放在了循环的外边
代码
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
bool chessboard[30][30];
int step,p,q;
vector<pair<char,int> > path;
int dy[8]={-2,-2,-1,-1,1,1,2,2},dx[8]={-1,1,-2,2,-2,2,-1,1};
bool dfs(int a,int b,int step)
{
chessboard[a][b]=1;
path.push_back(make_pair('A'+b,a+1));
if(step==p*q)
return true;
for(int i=0;i<8;i++)
{
int x=dx[i]+a,y=dy[i]+b;
if(x>=0&&x<p&&y>=0&&y<q&&chessboard[x][y]==0)
{
if(dfs(x,y,step+1))
return true;
}
}
chessboard[a][b]=0;
path.pop_back();
return false;
}
int main(void)
{
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
step=0;
memset(chessboard,0,sizeof(chessboard));
path.clear();
cin>>p>>q;
bool flag;
cout<<"#"<<i<<":"<<endl;
for(int i=0;i<q;i++)
{
for(int j=0;j<p;j++)
{
if(dfs(j,i,1))
{
for(auto it:path)
cout<<it.first<<it.second;
cout<<endl;
flag=1;
break;
}
}
if(flag)
break;
}
if(!flag)
cout<<"none"<<endl;
}
}
[深度优先搜索]寻找林克的记忆2(数独)
题目
题目要求
要求尽量时间短,应对方法是先从可能放置数字数量最少的位置开始搜索
数据结构
用char数组str来存储每个数组,row,col,block三个数组用来当作状态数组,map,ones两个打表数组,前者来跟lowbit搭配看二进制数字最后一个1在什么位置,后者填充某个数字有多少个1.
算法
在读入的时候,如果是数字,改变状态数组,如果是空位count++,进入dfs,递归出口count==0,get函数返回可以填写的数字的二进制表示,lowbit和map算出个数,遍历所有位置来找到最小的那个,记为x,y.然后正常循环
代码
#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=9;
int ones[1<<N],map[1<<N];
int row[N],col[N],block[3][3];
char str[100];
inline int lowbit(int i)
{
return i&-i;
}
void table()
{
for(int i=0;i<N;i++)
map[1<<i]=i;
for(int i=0;i<1<<N;i++)
{
int s=0;
for(int j=i;j;j-=lowbit(j))s++;
ones[i]=s;
}
}
void init()
{
for(int i=0;i<N;i++) row[i]=col[i]=(1<<N)-1;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
block[i][j]=(1<<N)-1;
}
inline int get(int x,int y)
{
return row[x]&col[y]&block[x/3][y/3];
}
bool dfs(int count)
{
if(!count)
return true;
int minchoose=10;
int x,y;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(str[i*N+j]=='.'&&ones[get(i,j)]<minchoose)
{
minchoose=ones[get(i,j)];
x=i,y=j;
}
}
}
for(int i=get(x,y);i>0;i-=lowbit(i))
{
int t=map[lowbit(i)];
row[x]-=1<<t;
col[y]-=1<<t;
block[x/3][y/3]-=1<<t;
str[x*N+y]=t+'1';
if(dfs(count-1))return true;
row[x]+=1<<t;
col[y]+=1<<t;
block[x/3][y/3]+=1<<t;
str[x*N+y]='.';
}
return false;
}
int main(void)
{
table();
while(cin>>str,str[0]!='e')
{
init();
int count=0;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(str[i*N+j]!='.')
{
int t=str[i*N+j]-'1';
row[i]-=1<<t;
col[j]-=1<<t;
block[i/3][j/3]-=1<<t;
}
else count++;
}
}
dfs(count);
cout<<str<<endl;
}
}
[Dijkstra]堆优化版
题目
数据结构
用邻接表存储图(适合稀疏图),用priority-queue表示堆,dist数组表示顶点到每个点的最短距离
st数组表示什么点已经被包涵到集合里了
算法
三步走:看看哪个点的距离最短,将其收入st,更新这个点所连接点的距离.
代码
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int>PII;
const int N=150010;
int h[N],e[N],ne[N],idx=1;
int w[N];
int n,m;
bool st[N];
int dist[N];
void add(int x,int y,int c)
{
w[idx]=c;
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
int dij()
{
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII> >heap;
heap.push(make_pair(0,1));
while(heap.size())
{
//第一步
auto it=heap.top();
heap.pop();
int distance=it.first,point=it.second;
//第二步
if(st[point]==1)continue;
else st[point]=1;
//第三步
for(int i=h[point];i!=-1;i=ne[i])
{
int t=e[i];
if(dist[t]>distance+w[i])
{
dist[t]=distance+w[i];
heap.push(make_pair(dist[t],t));
}
}
}
if(dist[n]==0x3f3f3f3f)return -1;
else return dist[n];
}
int main(void)
{
cin>>n>>m;
memset(dist,0x3f,sizeof(dist));
memset(h,-1,sizeof(h));
for(int i=0;i<m;i++)
{
int x,y,c;
cin>>x>>y>>c;
add(x,y,c);
}
cout<<dij();
}
[宽度优先搜素]滚石柱
题目
数据结构
定义dist数组(三维),作用类似于之前的表示坐标的数组,只不过加上了三种状态(立为0,竖为1,横为2),d数组表示变化量,state类型结构体表示物体目前的状态信息和坐标信息
算法
就是典型的宽度搜索
代码
#include <queue>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 510;
int n, m;
char g[N][N];
int dist[N][N][3];
struct state
{
int x;
int y;
int type;
};
int d[3][4][3] =
{
{{-2, 0, 1}, {0, 1, 2}, {1, 0, 1}, {0, -2, 2}}, // 0状态的
{{-1, 0, 0}, {0, 1, 1}, {2, 0, 0}, {0, -1, 1}}, // 1状态的
{{-1, 0, 2}, {0, 2, 0}, {1, 0, 2}, {0, -1, 0}} // 2状态的
};
int bfs(state start, state end)
{
memset(dist, -1, sizeof(dist));
dist[start.x][start.y][start.type] = 0;
queue<state> q;
q.push(start);
while (q.size())
{
state it = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int x = it.x + d[it.type][i][0];
int y = it.y + d[it.type][i][1];
int type = d[it.type][i][2];
if (type == '0' && g[x][y] == 'E')
continue;
if (x < 0 || x >= n || y < 0 || y >= m||g[x][y]=='#')
continue;
if (type == '1' && ((x + 1) >= n||g[x+1][y]=='#'))
continue;
if (type == '2' &&((y + 1) >= m||g[x][y+1]=='#'))
continue;
if (dist[x][y][type] != -1)
continue;
dist[x][y][type] = dist[it.x][it.y][it.type] + 1;
state temp={x,y,type};
q.push(temp);
}
}
return dist[end.x][end.y][end.type];
}
int main(void)
{
while (cin >> n >> m, n != 0 && m != 0)
{
int x_t, y_t;
state start,end;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
cin >> g[i][j];
if (g[i][j] == 'X')
{
x_t = i;
y_t = j;
}
if (g[i][j] == 'O')
end.x=i,end.y=j,end.type=0;
}
}
if (g[x_t][y_t + 1] == 'X')
start.x=x_t,start.y=y_t,start.type=2;
else if (g[x_t + 1][y_t] == 'X')
start.x=x_t,start.y=y_t,start.type=1;
else
start.x=x_t,start.y=y_t,start.type=0;
int t = bfs(start, end);
if (t == -1)
cout << "Impossile" << endl;
else
cout << t << endl;
}
}