一、互不攻击的士兵
你有一个含n个格子的一维棋盘。你可以往棋盘上放任意多个士兵。(每个格子只能放置一个士兵)。一个士兵会攻击与他相邻的左右两个格子中的士兵。现在问你,有多少种放置的方案使得棋盘上的士兵互不攻击?
我们以0代表格子里无士兵,1代表格子里有士兵。 一个格子两种状态0,1,两个格子00、01、10,三个格子000、001、010、100、101,四个格子0000、0001、0010、0100、1000、1001、1010当然要做这个题目很简单,直接找规律就可以了,但是因为是要写实验报告的、还是按照实验流程来吧
嗯?三个格子0|00,0|01,0|10,10|0,10|1
四个格子0|000,0|001,0|010,0|100,0|101,10|00,10|01,10|10
- 划分状态(划分子问题):对于本题来说,可以认为n个格子的最优解由0和n-1个格子的以及10和n-2个格子状态组成
- 状态表示:我们用f(n)表示n个格子的放置方案
- 状态转移方程:f(n)=f(n-1)+f(n-2)
- 确定边界:最小的问题就是1个格子和2个格子,而且不可再分,所以边界值n=1 and n=2
既然已经确定了状态转移方程和边界值,接下来直接解题便是.
#include<iostream>
using namespace std;
int f(int n){
if(n==1) return 2;
else if(n==2) return 3;
else return f(n-1)+f(n-2);
}
int main(){
int n;
cin>>n;
cout<<f(n);
return 0;
}
上面就是原生态的代码,当然考虑到效率问题和优化问题,我们使用一维数组f[n]模拟棋盘,会更快一些,而且储存了每个状态的最优解.
#include<iostream>
using namespace std;
int f[50]={2,3};
int main(){
for(int i=2;i<50;i++){
f[i]=f[i-1]+f[i-2];
}
int n;
cin>>n;
cout<<f[n-1];
return 0;
}
效果很明显,优化前
优化后:
二、爬楼梯
有一天,两人比赛从第1层开始,看谁能更快到达第m层,A一层一层往下走,这显然太慢了,为了公平起见,于是B给出了多个单向快捷通道,可以只使用1s就能从第u层到达第v层。已知到相邻的层需要1s,可以前进到下一层也可以回退到上一层,只能在【1,m】层之间转移,即不能到0或m+1层等层数。请你帮助A计算出到达第m层所需要的最少时间,战胜B (原题不正经,稍微改了一下)。
这题有点像图的最短路径,特殊的是每个边的权值都是1。官方提示说用Floyd算法,然而我根本不记得,直接bfs解决。但是实验要求动态规划,我还是写一下吧。Floyd算法看下面的链接。
Floyd解法
- 简单来说对Floyd算法,首先建立邻接矩阵dp[n][n],对Floyd算法来说,状态转移方程为d[i][j]=min{d[i][j],d[i][k]+d[k][j]}
算法如下
#include<iostream>
#define INF 1000
using namespace std;
int dp[202][202]={0};
int main(){
//初始化
int n,m;
cin>>n>>m;
for(int i=0;i<201;i++){
for(int j=0;j<201;j++){
if(i+1==j||i-1==j)
dp[i][j]=1;
else
dp[i][j]=INF;
}
}
int i,j;
while(n--){
cin>>i>>j;
dp[i][j]=1;
}
//算法
for(int k=1;k<=m;k++)
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(dp[i][j]>dp[i][k]+dp[k][j])
dp[i][j]=dp[i][k]+dp[k][j];
cout<<dp[1][m];
return 0;
}
Floyd的时间复杂度是 ,换成广度优先bfs的话,时间复杂度为
BFS(广度优先)
算法思想是 首先入队第一个顶点,遍历队首所有能抵达的地方,将其入队,之后队首出队,继续上述操作直至队列为空(所有顶点已走完),因为该题目的特殊性,所有路径长度唯一。
算法如下
// 构建邻接矩阵
#include<iostream>
#define INF 1000
using namespace std;
int visit[202];
int a[202][202]={0};
void print(int m){
for(int i=1;i<=m;i++){
cout<<visit[i]<<" ";
}cout<<endl;
}
int queue[1000];
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<201;i++){
a[i][i+1]=a[i+1][i]=1;
visit[i]=INF;
}
int i,j;
while(n--){
cin>>i>>j;
a[i][j]=1;
}
visit[1]=0;
int f=0,e=0;
queue[e++]=1;
while(f<e){
int x=queue[f];
int y=visit[x];
for(i=1;i<=m;i++){
if(a[x][i]!=0){
if(visit[i]>y+1){
visit[i]=y+1;
queue[e++]=i;
}
}
}
f++;
}
cout<<visit[m];
return 0;
}
显然比Floyd快一些。