第一周训练
训练主要内容:动态规划、模拟
第一题 特殊的正方形
题目1
生成由+和.组成的小正方形隔行排列形成的正方形
思路1
写一层循环,用于循环每层,次数与边长相等。
层数是奇数时说明这一层是+,是偶数时说明这一层是.。
在循环内为填写属于该小正方形的字符。
代码1
#include<iostream>
using namespace std;
char c[101][101];
int main()
{
int n;
cin >> n;
//外层循环
for (int i = 1; i <= n; i++)
{
//判断奇偶数
char t = i % 2 == 0 ? '.' : '+';
//填写每个小正方形的四条边
for (int j = i; j <= n - i + 1; j++)
{
c[i][j] = t;
c[j][i] = t;
c[n - i + 1][j] = t;
c[j][n - i + 1] = t;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cout << c[i][j];
}
cout << endl;
}
return 0;
}
第二题 走楼梯2
题目2
一次可以走一级或两级楼梯,连续最多走2次两级楼梯,问走到第n级有多少种走法。
思路2
走楼梯的种类是常见的动态规划的题目。
状态:走到第i级和连续走了两级j次。
状态转移方程:f(i, 0) = f(i - 1, 0 / 1 / 2); f(i, 1) = f(i - 2, 0); f(i, 2) = f(i - 2, 1)
初始条件:走上第一级有一种方案。
代码2
#include<iostream>
using namespace std;
//存放状态,第一维是第i级,第二维是走了两级j次
long long ans[52][4];
int n;
int main()
{
cin >> n;
//初始条件
ans[0][0] = 1;
ans[1][0] = 1;
for (int i = 2; i <= n; i++)
{
//状态转移方程
ans[i][0] += (ans[i - 1][0] + ans[i - 1][1] + ans[i - 1][2]);
ans[i][1] += ans[i - 2][0];
ans[i][2] += ans[i - 2][1];
}
cout << ans[n][0] + ans[n][1] + ans[n][2] << endl;
return 0;
}
第三题 走路
题目3
你位于0的位置,接下来n次可以向右走ai或者bi步,问n次之后能走到的位置。
思路3
模拟题。用一个数组存放0 - m所有点是否能走到,每次走的时候按照前次结果寻找这次能走到的地方。
因为只涉及了两次的结果,所以可以用滚动数组的方法存放能走到的位置。
代码3
#include<iostream>
#include<cstring>
using namespace std;
//m最大值
constexpr int MMax = 1e5 + 5;
//能走到的位置,第二维的2表示最近两次的结果
bool a[MMax][2];
int n, m;
int ai, bi;
int main()
{
cin >> n >> m;
//初始位置
a[0][0] = true;
for (int i = 1; i <= n; i++)
{
cin >> ai>> bi;
//判断前一次存放的位置
int p = i % 2;
//判断这一次存放的位置
int pp = (p == 0) ? 1 : 0;
//遍历所有前一次走到的点,推算这次能走到的点
for (int j = 0; j <= m; j++)
{
if (a[j][pp])
{
a[j + ai][p] = true;
a[j + bi][p] = true;
}
}
//推算完,将前一次的数据清空,防止影响下一次存储
for (int j = 0; j <= m; j++)
{
a[j][pp] = false;
}
}
//输出
int p = n % 2;
for (int i = 0; i <= m; i++)
{
cout << a[i][p] ? 1 : 0;
}
return 0;
}
第四题 简单分数统计
题目4
有n个人参加m个题目的比赛,需要通过平台的提交记录(包含别的人和别的题目)判分。
思路4
依然是简单的模拟。可以用map存放每个人得的分和每到题的分值。遇到名字和题目都存在的记录就计分。
代码4
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int main()
{
//题目的分值
map<string, int> qscore;
//学生的分数
map<string, int> sscore;
//所有的学生,用于最后的输出
vector<string> idlist;
int n, m, k;
cin >> n >> m >> k;
//存放学生
for (int i = 0; i < n; i++)
{
string id;
cin >> id;
//每个学生初始分数为0
sscore.insert(map<string, int>::value_type(id, 0));
idlist.push_back(id);
}
//存放题目
for (int i = 0; i < m; i++)
{
string q;
int score;
cin >> q >> score;
qscore.insert(map<string, int>::value_type(q, score));
}
//读取记录
for (int i = 0; i < k; i++)
{
string s, q, r;
cin >> s >> q >> r;
//学生存在、题目存在并且答案正确,才需要计分
if (sscore.find(s) != sscore.end() && qscore.find(q) != qscore.end() && r == "AC")
{
sscore[s] += qscore[q];
}
}
//逐个输出
for (int i = 0; i < n; i++)
{
cout << idlist[i] << ' ' << sscore[idlist[i]] << endl;
}
return 0;
}
第五题 Alice的德州扑克
题目5
有6种牌型,需要判断牌型。
思路5
按照不同牌型的要求判断即可。
代码5
#include<iostream>
using namespace std;
int main()
{
int point[5], suit[5];
for (int i = 0; i < 5; i++)
{
cin >> point[i];
}
for (int i = 0; i < 5; i++)
{
cin >> suit[i];
}
//同花顺
if (point[0] == point[1] - 1 && point[1] == point[2] - 1 && point[2] == point[3] - 1 && point[3] == point[4] - 1
&& suit[0] == suit[1] && suit[1] == suit[2] && suit[2] == suit[3] && suit[3] == suit[4])
{
//判断是否为皇家同花顺
if (point[4] == 14)
{
cout << "ROYAL FLUSH" << endl;
}
else
{
cout << "STRAIGHT FLUSH" << endl;
}
}
//四条,只有两种情况:前4张点数相同或后4张点数相同
else if ((point[0] == point[1] && point[1] == point[2] && point[2] == point[3]) || (point[1] == point[2] && point[2] == point[3] && point[3] == point[4]))
{
cout << "FOUR OF A KIND" << endl;
}
//葫芦,只有两种情况:前三张和后两张分别点数相同或前两张和后三张分别点数相同
else if ((point[0] == point[1] && point[1] == point[2] && point[3] == point[4]) || (point[2] == point[3] && point[3] == point[4]) && point[0] == point[1])
{
cout << "FULL HOUSE" << endl;
}
//同花
else if (suit[0] == suit[1] && suit[1] == suit[2] && suit[2] == suit[3] && suit[3] == suit[4])
{
cout << "FLUSH" << endl;
}
//顺子
else if (point[0] == point[1] - 1 && point[1] == point[2] - 1 && point[2] == point[3] - 1 && point[3] == point[4] - 1)
{
cout << "STRAIGHT" << endl;
}
//其余情况
else
{
cout << "FOLD" << endl;
}
return 0;
}
第六题 任务分配
题目6
有n个任务,有不同的开始时间和结束时间,还有收益。问怎样收益最大。
思路6
是一道动态规划。
状态:结束时间i
状态转移方程:f(i) = max(f(si) + wi(si和wi是每一个在i时结束的任务), f(i - 1))
代码6
#include<iostream>
#include<queue>
using namespace std;
constexpr int maxn = 1e3 + 1;
constexpr int maxt = 1e3 + 1;
int n;
int si[maxn], ei[maxn], wi[maxn];
int ans[maxt];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> si[i] >> ei[i] >> wi[i];
}
//状态i
for (int i = 1; i <= 1000; i++)
{
//在执行到此处时已经能保证,i时刻已经结束的情况都已考虑
//只需要再考虑i时刻没有任务的情况
ans[i] = max(ans[i], ans[i - 1]);
//遍历在i时刻开始的任务,更新在i时刻之后的状态
for (int j = 1; j <= n; j++)
{
if (si[j] == i)
{
ans[ei[j]] = max(ans[ei[j]], ans[i] + wi[j]);
}
}
}
cout << ans[1000] << endl;
return 0;
}
第七题 路径计数
题目7
一个网格上,有些格子可以通行。问从左上角走到左下角的路径数。
思路7
还是动态规划的题目。
状态:位置(i, j)
状态转移方程:f(i, j) = f(i - 1, j) + f(i, j - 1)
起始条件:f(1, 1) = 1
代码7
#include<iostream>
using namespace std;
//最大边长和取模值
constexpr int maxn = 102, mod = 1e9 + 7;
int n;
//存放网格状态
bool map[maxn][maxn];
//存放dp结果
int ans[maxn][maxn];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> map[i][j];
}
}
//起始条件
ans[1][1] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
//(1, 1)时已有结果
if (i == 1 && j == 1)
{
continue;
}
//该点不能通过
if (!map[i][j])
{
continue;
}
//左侧边,只能从上面走
else if (j == 1)
{
ans[i][j] += ans[i - 1][j];
}
//上侧边,只能从左面走
else if (i == 1)
{
ans[i][j] += ans[i][j - 1];
}
//普通情况
else
{
ans[i][j] += ans[i][j - 1] + ans[i - 1][j];
}
ans[i][j] %= mod;
}
}
cout << ans[n][n] << endl;
return 0;
}
第八题 最大和上升子序列
题目8
求一组数中和最大的上升的子序列。(子序列:按原顺序选出一组数组成的新序列)
思路8
最x的子序列都是属于动态规划。
状态:最后一个数是第i个数
状态转移方程:f(i) = max(f(j) + ai (j在i前且aj < ai), ai)
起始条件:f(1) = a1
代码8
#include<iostream>
using namespace std;
constexpr int maxn = 1001;
int a[maxn];
int ans[maxn];
int maxans = 0;
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
//起始条件
ans[1] = a[1];
for (int i = 2; i <= n; i++)
{
//寻找每一个在i前的j,并且ai > aj
for (int j = 1; j < i; j++)
{
if (a[i] > a[j])
{
//更新
ans[i] = max(ans[i], ans[j] + a[i]);
}
}
//还有一种情况是从当前数开始一个新序列
ans[i] = max(ans[i], a[i]);
maxans = max(ans[i], maxans);
}
cout << maxans << endl;
return 0;
}