算法导航:
P1797 求解田忌赛马问题(分析)
P1796 求解马走棋问题
P1752 求解掷色子游戏问题
B1631 [Usaco2007 Feb]Cow Party
B3408 [Usaco2009 Oct]Heat Wave 热浪
P1747 求解递增序列中与x最接近元素问题
P1795 求解图的 m 着色问题(分析)
P1793 求解迷宫问题
算法1:(贪心算法)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;cin>>n;int a[n],b[n],ans=0;
for(int i=0;i<n;i++) cin>>a[i];
for(int j=0;j<n;j++) cin>>b[j];
sort(a,a+n);sort(b,b+n);
int lefta=0,leftb=0,righta=n-1,rightb=n-1;
while(lefta<=righta){
if(a[righta]>b[rightb]){
ans+=200;righta--;rightb--;
}
else if(a[righta]<b[rightb]){
ans-=200;lefta++;rightb--;
}
else{
if(a[lefta]>b[leftb]){
ans+=200;lefta++;leftb++;
}
else{
if(a[lefta]<b[rightb]) ans-=200;lefta++;rightb--;
}
}
}
cout<<ans<<endl;return 0;
}
图中源代码:
//问题表示
int n;
int a[MAX];
int b[MAX];
//求解结果表示
int ans;
int main()
{ while (true)
{ scanf("%d",&n);
if (n==0) break;
for (int i=0;i<n;i++)
scanf("%d",&a[i]);
for (int j=0;j<n;j++)
scanf("%d",&b[j]);
solve();
printf("%d\n",ans);
}
return 0;
}
void solve() //求解算法
{ sort(a,a+n); //对a递增排序
sort(b,b+n); //对b递增排序
ans=0;
int lefta=0,leftb=0;
int righta=n-1,rightb=n-1;
while (lefta<=righta) //比赛直到结束
{ if (a[righta]>b[rightb])
//田忌最快的马比齐威王最快的马快,两者比赛
{ ans+=200;
righta--;
rightb--;
}
else if (a[righta]<b[rightb])
//田忌最快的马比齐威王最快的马慢
{ ans-=200;
//选择田忌最慢的马与比齐威王最快的马比赛
lefta++;
rightb--;
}
else //田忌最快的马与齐威王最快的马的速度相同
{ if (a[lefta]>b[leftb])
//田忌最慢的马比齐威王最慢的马快,两者比赛
{ ans+=200;
lefta++;
leftb++;
}
else
{ if (a[lefta]<b[rightb])
//否则,用田忌最慢的马与齐威王最快的马比赛
ans-=200;
lefta++;
rightb--;
}
}
}
}
思想:
采用递归的方式写算法。除了输入相应的m和n以外,核心在于递归调用dfs函数,同时通过全局变量ans来计数,确定到底有几条路线可以到达。
递归中如果越界则直接终止执行。如果输入的两个数恰好等于m和n,则此时已经到达了终点,那么ans++之后终止即可。如果没有到达终点。我们分别递归调用其两个可以行走的位置继续进行新一轮的子递归循环判断。
源代码部分:
//题目说的很清楚,可以直接暴力
#include<iostream>
using namespace std;
int n, m,ans;
void dfs(int x, int y) //dfs
{
if (x > m && y > n) return; //判断是否越界
if (x == m && y == n) //判断是否能够到终点
{
ans++;
return;
}
dfs(x + 1, y + 2); //对马的下两个可能到的位置分别进行深搜
dfs(x + 2, y + 1);
}
int main()
{
cin >> n >> m;
dfs(1, 1);
cout << ans << endl;
return 0;
}
这题可以直接枚举一些情况 然后可以发现数学规律。则直接可以输出结果。
源代码部分:
#include<iostream>
#include<cmath>
using namespace std;
//这题自己模拟一下,就能发现规律了,直接输出2^(n-1)
int main()
{
int n;
cin >> n;
cout << pow(2, n - 1) << endl;
return 0;
}
本题考点:最短路径问题
对于最短路径问题,一般有两种算法比较常见。第一种是Dijkstra(迪杰斯特拉)算法 其本质是贪心算法。另一种是Floyd(弗洛伊德)算法,其本质是动态规划算法。
接下来将通过两种方法都实现一下本题的结果。
(接下来先提供迪杰斯特拉的标准算法模型):
/* B1631 Cow Party
Dijkstra算法 By:Simon
*/
#include<bits/stdc++.h>
using namespace std;
int n; //表示牛的数量
int m; //表示道路数量m
int x; //表示目标派对所在的农场编号x
int edges[1000][1000]; //edges[i][j] 用于存储i到j的距离
int dist[1000]; //记录当前点到路径起点的距离
int visited[1000]; //记录当前的点是否已经被初始化了
//int parent[1000]; //用于存储每个节点对应的父节点的数值
int dijkstra(int n, int m){
for (int i = 1; i <= n; i++) { // 每次循环都会剔除掉1个点,因此需要for循环遍历n次。
int index = -1; // index代表当前未被访问的距离原点最近的点
dist[1] = 0; // 原点到原点的距离为0,这个很重要,否则下面for循环所有的dist都是0x3f3f3f3f,无法找出index。
for (int j = 1; j <= n; j++) { // find the index of min distance
if (!visited[j] && (index == -1 || dist[j] < dist[index])) { // 当前的点没有被踢出,并且当前点的距离比index点的距离小,则更新index。index == -1表示还未开始找到dist中最小值,则把dist[1]加入。
index = j;
}
}
visited[index] = 1; //找到当前距离原点最小值的点,则把点进行标记踢出。
for (int j = 1; j <= n; j++) {
if (dist[index] + edges[index][j] < dist[j]) { //index点更新与它相连的所有点。
dist[j] = dist[index] + edges[index][j];
}
}
}
if (dist[n] == 0x3f3f3f3f) { //如果没有到n点的路径,则返回-1
return -1;
}
return dist[x];
}
int main(){
memset(edges, 0x3f, sizeof(edges));
cin>>n>>m>>x;
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
edges[u][v]=w;
}
memset(dist,0x3f,sizeof(dist));
memset(visited,0,sizeof(visited)); //访问判断数组 初始状态都将值设置为false
//memset(parent,-1,sizeof(parent));
cout<<dijkstra(n,m)<<endl;
return 0;
}
之后我们根据题目不同的要求修改算法。比如本题要求对每头牛的一个来回的路径进行求最大值的需求,我们可以适当的修改算法。
1.迪杰斯特拉算法(将路径数组对调以后再次调用算法函数 就可以实现换一个方向的统计)
(PS:内容空间开1000并不满足。经过测试开1010可以通过所有用例)
/* B1631 Cow Party
Dijkstra算法 By:Simon
*/
#include<bits/stdc++.h>
using namespace std;
int n; //表示牛的数量
int m; //表示道路数量m
int x; //表示目标派对所在的农场编号x
int edges[1010][1010]; //edges[i][j] 用于存储i到j的距离
int edges2[1010][1010];
int dist[1010]; //记录当前点到路径起点的距离
int dist2[1010]; //记录各自回相应农场时的最短路径的数值
int visited[1010]; //记录当前的点是否已经被初始化了
void dijkstra1(int start){ //同算法 但是修改了对应的数组 所以可以表示牛去派对的最短路径的统计
memset(visited,0,sizeof(visited)); //访问判断数组 初始状态都将值设置为false
for(int i=1;i<=n;i++){
int index=-1;
dist[start]=0;
for(int j=1;j<=n;j++){
if(!visited[j] && (index==-1 || dist[j]<dist[index])){
index=j;
}
}
visited[index]=1;
for(int j=1;j<=n;j++){
if(dist[index]+edges[index][j]<dist[j]){
dist[j]=dist[index]+edges[index][j];
}
}
}
}
void dijkstra2(int start){ //从start点开始到其它所有点的最短路径存储在dist[]中 可以表示牛回家的最短路径的统计
memset(visited,0,sizeof(visited)); //访问判断数组 初始状态都将值设置为false
for(int i=1;i<=n;i++){
int index=-1;
dist2[start]=0;
for(int j=1;j<=n;j++){
if(!visited[j] && (index==-1 || dist2[j]<dist2[index])){
index=j;
}
}
visited[index]=1;
for(int j=1;j<=n;j++){
if(dist2[index]+edges2[index][j]<dist2[j]){
dist2[j]=dist2[index]+edges2[index][j];
}
}
}
}
int main(){
memset(edges, 0x3f, sizeof(edges));
memset(edges2, 0x3f, sizeof(edges2));
cin>>n>>m>>x;
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
edges2[u][v]=w;
}
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) edges[j][i]=edges2[i][j];
memset(dist,0x3f,sizeof(dist));
memset(dist2,0x3f,sizeof(dist2));
dijkstra2(x);
dijkstra1(x);
int maxs=-1;
for(int i=1;i<=n;i++){
maxs=max(dist[i]+dist2[i],maxs);
}
cout<<maxs<<endl;
return 0;
}
无注释简约版:
//朴素版dijkstra
#include <iostream>
#include <algorithm>
#include <string.h>
#include <cmath>
using namespace std;
const int N=1010;
int f[N][N];
int F[N][N];
int dist1[N],dist2[N];
bool s1[N],s2[N];
int n,m,k;
void dijkstra1(int k)
{
memset(dist1,0x3f,sizeof dist1);
dist1[k]=0;
for(int i=1;i<=n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!s1[j]&&(t==-1||dist1[t]>dist1[j]))t=j;
}
s1[t]=true;
for(int i=1;i<=n;i++)
{
dist1[i]=min(dist1[i],dist1[t]+f[t][i]);
}
}
}
void dijkstra2(int k)
{
memset(dist2,0x3f,sizeof dist2);
dist2[k]=0;
for(int i=1;i<=n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!s2[j]&&(t==-1||dist2[t]>dist2[j]))t=j;
}
s2[t]=true;
for(int i=1;i<=n;i++)
{
dist2[i]=min(dist2[i],dist2[t]+F[t][i]);
}
}
}
int main()
{
cin>>n>>m>>k;
memset(f,0x3f3f3f3f,sizeof f);
memset(F,0x3f3f3f3f,sizeof F);
while(m--)
{
int x,y,z;
cin>>x>>y>>z;
f[x][y]=min(f[x][y],z);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
F[i][j]=f[j][i];
}
}
dijkstra1(k);
dijkstra2(k);
int summax=-1;
for(int i=1;i<=n;i++)
{
summax=max(dist1[i]+dist2[i],summax);
}
cout<<summax<<endl;
return 0;
}
大佬源代码:
链接:[Usaco2007 Feb]Cow Party 奶牛派对__Destiny__Fate_(DLS)的博客-CSDN博客
#include<cstdio>
#define INF 10000005
using namespace std;
const int N=1001;
int n,m,x,dist[N],val[N][N],g[N][N],num[N],team[20001],adda,addb,costi,a[N];
bool ex[20001];
void init()//读入
{
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&adda,&addb,&costi);
val[adda][addb]=costi;//双向边邻接矩阵
g[adda][++num[adda]]=addb;
g[addb][++num[addb]]=adda;
}
}
void SPFA(int s)//SPFA
{
for(int i=1;i<=n;i++)
{
dist[i]=INF;
ex[i]=false;
team[i]=false;
}//初始化
int head=0,tail=1,u;
team[1]=s,dist[s]=0,ex[s]=true;
while(head!=tail)
{
head++;
head=((head-1)%1601)+1;
u=team[head];
ex[u]=false;
for(int i=1;i<=num[u];i++)//枚举与u相连的点
if(dist[g[u][i]]>dist[u]+val[u][g[u][i]])
{
dist[g[u][i]]=dist[u]+val[u][g[u][i]];
if(!ex[g[u][i]])
{
tail++;
tail=((tail-1)%1601)+1;
team[tail]=g[u][i];
ex[g[u][i]]=true;
}
}
}
}
void GK_sb()
{
for(int i=1;i<=n;i++)
{
SPFA(i);
a[i]=dist[x];//用a数组保存i到x的最短路
}
SPFA(x);
int ans=-1;
for(int i=1;i<=n;i++)
{
//printf("%d\n",a[i]+dist[i]);
if(a[i]+dist[i]<INF&&a[i]+dist[i]>ans)
ans=a[i]+dist[i];//更新最大值
}
printf("%d\n",ans);
}
int main()
{
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
val[i][j]=INF;
}
init();
GK_sb();
return 0;
}
大佬源代码:
链接:[Usaco2009 Oct]Heat Wave 热浪_Recurss的博客-CSDN博客
/*
* @Description: To iterate is human, to recurse divine.
* @Autor: Recursion
* @Date: 2022-03-02 11:52:08
* @LastEditTime: 2022-03-24 12:07:32
*/
#include <bits/stdc++.h>
#define Inf 2147483647
using namespace std;
int head[(int)1e6+10],cnt;
int dis[(int)1e6+10],vis[(int)1e6+10];
int n,m,s;
void init()
{
for(int i = 1;i <= n;i ++)
dis[i] = Inf;
}
struct edge{
int to;
int weight;
int next;//终点,边权,同起点的上一条边的编号
}edge[(int)1e6+10];
struct node{
int w;
int now;
//重载运算符把最小的元素放在堆顶(大根堆)
bool operator <(const node &x)const
{
return w > x.w;
}
};
priority_queue<node>q;
void add(int u,int v,int w)
{
edge[++cnt].to = v;
edge[cnt].weight = w;
edge[cnt].next = head[u];//存储该点的下一条边
head[u] = cnt;//更新目前该点的最后一条边(就是这一条边)
}
void dijkstra()
{
for(int i = 1;i <= n;i ++)
dis[i] = Inf;
dis[s] = 0;
q.push((node){0,s});
while(!q.empty()){
//堆为空所以节点都更新
node x = q.top();
q.pop();
int u = x.now;
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u];i;i = edge[i].next){
int v = edge[i].to;
if(dis[v] > dis[u] + edge[i].weight){
dis[v] = dis[u] + edge[i].weight;
q.push((node){dis[v],v});
}
}
}
}
int main()
{
int t;
cin >> n >> m >> s >> t;
init();
for(int i = 1;i <= m;i++)
{
int x,y,z;
cin >> x >> y >> z;
add(x,y,z);
add(y,x,z);
}
//dijkstra();
int pos = s;
dis[pos] = 0;
while(vis[pos] == 0){
int minn = Inf;
vis[pos] = 1;
for(int i = head[pos];i!=0;i = edge[i].next){
if(!vis[edge[i].to]&&(dis[edge[i].to] > dis[pos] + edge[i].weight))
dis[edge[i].to] = dis[pos] + edge[i].weight;
}
for(int i = 1;i<=n;i++)
if(dis[i] < minn&&vis[i] == 0){
minn = dis[i];
pos = i;
}
}
cout << dis[t] << endl;
// for(int i = 1;i <= n;i ++)
// cout << dis[i] << " ";
}
弗洛伊德算法:(算例时间比较长)
#include<bits/stdc++.h>
#define MAX 2147483647
using namespace std;
int n,m,s,t;
int main(){
cin>>n>>m>>s>>t;
long long path[n+1][n+1];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
path[i][j]=MAX;
int u,v;long long w;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
path[u][v]=min(path[u][v],w);
path[v][u]=min(path[v][u],w);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
path[j][k]=min(path[j][k],path[j][i]+path[i][k]);
cout<<path[s][t]<<endl;
return 0;
}
我的源代码:
算法思路是二分查找算法
#include <bits/stdc++.h>
using namespace std;
//a数组用于存储相应的n个整数 b数组用于存储相应的待测试数据
void f(int a[], int n, int m, int b[]){
for(int i = 0; i < m; i++){ //b[i]即当前状态下的待测试数据
if(a[0] > b[i]){ //最小值比测试数据大 则最小值为最近的
b[i] = a[0];
continue;
}
if(a[n -1] < b[i]){ //最大值比测试数据小 则最大值为最接近的
b[i] = a[n -1];
continue;
}
int l = 0, r = n -1;
int mid;
while(l < r){
mid = (l + r) / 2;
if(a[mid] >= b[i])
r = mid;
else
l = mid + 1;
}
if(a[l] == b[i] || (abs(a[l] - b[i]) < abs(a[l -1] -b[i])))
b[i] = a[l];
else if(abs(a[l] - b[i]) == abs(a[l -1] -b[i])){
b[i]=min(a[l],a[l-1]);
}else{
b[i] = a[l -1];
}
}
}
int main(){
int n, m;
cin >> n;
int a[n];
for(int i = 0; i < n; i++)
cin >> a[i];
cin >> m;
int b[m];
for(int i = 0; i < m; i++)
cin >> b[i];
f(a, n, m, b);
for(int i = 0; i < m; i++)
cout <<b[i]<<endl;
return 0;
}
改编算法:
#include<bits/stdc++.h>
using namespace std;
void functions(int a[],int b[],int n,int m){
for(int i=0;i<m;i++){ //b[i]为本轮待查数据
int l=0,r=n-1,mid;int temp=b[i];
if(b[i]<=a[l]) {b[i]=a[l]; continue;}
if(b[i]>=a[r]) {b[i]=a[r]; continue;}
while(l<=r){
mid=(l+r)/2;
if(a[mid]<=b[i]) l=mid+1;
else r=mid-1;
}
if(a[r]==temp) b[i]=a[r];
if(abs(a[r]-temp)<=abs(a[r+1]-temp)) b[i]=a[r];
else b[i]=a[r+1];
}
}
int main(){
int n,m,i;cin>>n>>m;int a[n],b[m];
for(i=0;i<n;i++) cin>>a[i];
for(i=0;i<m;i++) cin>>b[i];
functions(a,b,n,m);
for(i=0;i<m;i++) cout<<b[i]<<endl; return 0;
}
大佬源代码:
链接:P1747 求解递增序列中与x最接近元素问题_Recurss的博客-CSDN博客_求解递增序列中与x最接近元素问题
/*
* @Description: To iterate is human, to recurse divine.
* @Autor: Recursion
* @Date: 2022-04-13 10:59:17
* @LastEditTime: 2022-04-13 11:10:59
*/
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e9 + 10;
const int N = 1e6 + 10;
LL a[N];
int n,m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1;i <= n;i ++)
cin >> a[i];
cin >> m;
while(m--){
int q;
cin >> q;
int pos = lower_bound(a + 1, a + 1 + n,q) - a - 1;
if(pos == n){
cout << a[n] << endl;
continue;
}
else if(abs(q - a[pos]) > abs(q - a[pos + 1])){
cout << a[pos + 1] << endl;
}
else
cout << a[pos] << endl;
}
return 0;
}
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;
}
void dfs(int i) //求解图的m着色问题
{ if (i>n) //达到叶子结点
count++; //着色方案数增1
else
{ for (int j=1;j<=m;j++) //试探每一种着色
{ x[i]=j; //试探着色j
if (Same(i)) //可以着色j,进入下一个顶点着色
dfs(i+1);
x[i]=0; //回溯
}
}
}
自行设计源代码部分:
#include<bits/stdc++.h>
//求解图的m着色问题
using namespace std;
int n,k,m,counts=0;
int a[21][21];//存储邻接矩阵
int x[21];//存储着色的情况
bool Same(int i){
for(int j=1;j<=n;j++)
if(a[i][j]==1 && x[i]==x[j]) return false;
return true;
}
void dfs(int i){
if(i>n) counts++;
else{
for(int j=1;j<=m;j++){
x[i]=j;
if(Same(i)) dfs(i+1);
x[i]=0;
}
}
}
int main(){
cin>>n>>k>>m; //n为顶点数 k为边数 m为颜色数
for(int i=0;i<k;i++) {
int x,y;
cin>>x>>y;
a[x][y]=1;a[y][x]=1; //无向图 两个方向都被标记
}
dfs(1);
cout<<counts<<endl;
return 0;
}
大佬源代码:
参考链接:LevOJ P1795 求解图的m着色问题_Ordinary JZP的博客-CSDN博客
#include<iostream>
using namespace std;
int k, v, m; //v 顶点 k 边数 m 颜色种类
int ans; //记录最后答案
int col[1010]; //标记每个顶点所图颜色
int graph[1010][1010]; //二维数组存图
bool judge(int p, int color) //判断p点是否能图该颜色
{
int i, flag = 1;
// 遍历与p点相邻接的点,如果p点所图颜色与这些点相同,那么p点就不能图这个颜色
for (i = 1; i <= v; i++)
{
if (graph[p][i] == 1 && color == col[i]) return false;
}
return true;
}
void dfs(int vec) //深搜,可理解为给vec这点图颜色
{
//如果当前所图的点的编号大于总共给的点的个数,那么说明此种图色方法可行
if (vec > v)
{
ans++;
return;
}
int i;
//遍历所有颜色,并判断是否能图这种颜色
for (i = 1; i <= m; i++)
{
if (judge(vec, i))
{
col[vec] = i;
dfs(vec + 1);
col[vec] = 0; //回溯
}
}
}
int main()
{
cin >> v >> k >> m;
int i;
for (i = 1; i <= k; i++)
{
int x, y;
cin >> x >> y;
graph[x][y] = 1; //注意是无向图
graph[y][x] = 1;
}
dfs(1); //从顶点1开始图色
cout << ans << endl;
return 0;
}
分析:
这道题的算法考点是树的dfs深度优先搜索算法
大佬源代码:
参考链接:LevOJ P1793 求解迷宫问题_Ordinary JZP的博客-CSDN博客
#include<iostream>
#include<cstdio>
using namespace std;
char arr[10][10]; //存储图
int visit[10][10]; //判断是否访问过当前点并且存储最后找到的通路,0代表未访问,1代表已访问
int dx[] = { 0,1,0,-1 }; //方向数组,控制x的方向
int dy[] = { 1,0,-1,0 }; //方向数组,控制y的方向
void dfs(int x, int y) //深搜
{
if (x == 7 && y == 7) //结束判断标志,到达终点的时候就结束,遍历visit数组,如果为1,则为最终通路上上的一点,输出空格
{
int i, j;
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
if (visit[i][j] == 1) printf(" ");
else printf("%c", arr[i][j]);
}
printf("\n");
}
return;
}
int k; //根据上下左右四个方向来遍历
for (k = 0; k < 4; k++)
{
if (arr[x + dx[k]][y + dy[k]] == 'O' && visit[x + dx[k]][y + dy[k]] == 0) //判断条件,下一个节点可以通行,并且没有访问过
{
visit[x + dx[k]][y + dy[k]] = 1; //设该节点为已访问,遍历该节点
dfs(x + dx[k], y + dy[k]);
visit[x + dx[k]][y + dy[k]] = 0; //回溯
}
}
}
int main()
{
int i, j;
for (i = 0; i < 8; i++)
{
for (j = 0; j < 8; j++)
{
scanf("%c", &arr[i][j]);
}
getchar();
}
visit[0][0] = 1; //因为从起点开始,所以要设置起点为已访问。
dfs(0, 0);
return 0;
}