101 特殊的正方形
是一道模拟题 这里是找到了一种类似于小学数学规律题的解法 按照圈数和行/列数的对应关系来模拟
#include<bits/stdc++.h>
using namespace std;
char arr[105][105];
int n;
int i;
int main()
{
cin >> n;
if (n % 2 == 0)
i = n / 2;
else
i = n / 2 + 1;
//n = 5 i = 3
for (int j = 1; j <= i; j++)
{
//k = 1 k <= 5;
//k = 2 k <= 4;
//k = 3 k <= 3;
for (int k = j; k <= n + 1 - j; k++)
{
if (j % 2 == 0)
{
arr[j][k] = arr[k][j] = '.';
arr[n + 1 - j][k] = arr[k][n + 1 - j] = '.';
}
else if (j % 2 == 1)
{
arr[j][k] = arr[k][j] = '+';
arr[n + 1 - j][k] = arr[k][n + 1 - j] = '+';
}
}
}
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
printf("%c", arr[j][k]);
}
printf("\n");
}
return 0;
}
102 走楼梯2
是一道标准的DP题 在这里它和简单的走楼梯有一个不同点就是需要考虑不能连续三步上两个 对于这种状态的存储 个人感觉有两种存储方式
1、二维DP数组 DP[i] [j]用j去存储当前上楼梯的情况
2、DP[i]=DP[i-1]+DP[i-3]+DP[i-5]
#include<bits/stdc++.h>
using namespace std;
long long DP[55][3];
int n;
int main()
{
cin >> n;
DP[0][0] = 1;
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= 2; j++)
{
if (j < 2)
{
DP[i + 2][j + 1] += DP[i][j];
DP[i + 1][0] += DP[i][j];
}
else
{
DP[i + 1][0] += DP[i][j];
}
}
}
cout << DP[n][0] + DP[n][1] + DP[n][2];
return 0;
}
103 走路
想说的话都在代码里 按位或在存储大量布尔变量时的优美性加之左移运算符 与走路的状态模拟完美契合 优美!
#include<bits/stdc++.h>
using namespace std;
const int maxm = 1e5 + 5;
int a[105][2];
bitset<maxm>dp[105];
int n, m;
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i][0] >> a[i][1];
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
{
dp[i] |= dp[i - 1] << a[i][0];
dp[i] |= dp[i - 1] << a[i][1];
}
for (int i = 0; i <= m; i++)
cout << dp[n][i];
return 0;
}
//此题的优美就在于dp数组的选择,结合位图与按位或、左移运算符
//其中
//
//
//按位或的优美在于
//用来合并不同的分支(详情见资料一)
//不同的分支最后会统一记录在一个位模式下(本题的分支意指向右走a[i]和b[i]步)
//不同走的方式都应该被记录在dp中 最后一并输出
//
//
//左移运算符的优美在于
//使用左移运算符完美的模拟了人的走路
//如果人当前位置为0 假定是0001
//那么走三步 显然就是左移3 1000
104 简单分数统计
直接三层for循环O(N^3)应该会超时
这里可以用pair、结构体或者map这个STL容器做 前两种方法是利用一个数据存储多个信息降低了循环时的时间复杂度 map则是利用了本身键值对的映射关系和寻找元素的低时间复杂度O(logN)
#include<bits/stdc++.h>
using namespace std;
int n, m, k;
map<string, int>frd;
map<string, int>score;
string per[200 + 5];
int main()
{
cin >> n >> m >> k;
for (int i = 0; i < n; i++)
{
string temp;
cin >> temp;
per[i] = temp;
frd[temp] = 0;
}
for (int i = 0; i < m; i++)
{
string temp; int sco;
cin >> temp >> sco;
score[temp] = sco;
}
while (k--)
{
string name, ques, flag;
cin >> name >> ques >> flag;
if (flag == "WA")
continue;
//因为好朋友们只会提交比赛的题目 如果题目AC且提交人为好朋友 那么就不用判断
//好朋友提交的是什么题目 一定存在于题目列表中
if (frd.count(name))
{
frd[name] += score[ques];
}
}
for (int i = 0; i < n; i++)
cout << per[i] << " " << frd[per[i]] << endl;
return 0;
}
105 Alice的德州扑克
纯模拟题 没什么好说的
#include<bits/stdc++.h>
using namespace std;
struct Card {
int score;
int color;
}card[5];
string ans;
bool cmp(Card a, Card b)
{
return a.score < b.score;
}
int cnt[16];
int main()
{
for (int i = 0; i < 5; i++)
cin >> card[i].score;
for (int i = 0; i < 5; i++)
cin >> card[i].color;
sort(card, card + 5, cmp);
//先考虑会不会是顺子
bool flag1 = true;
for (int i = 1; i < 5; i++)
{
if ((card[i].score - 1 )!= card[i - 1].score)
{
flag1 = false;
break;
}
}
//如果是顺子
if (flag1)
{
//考虑是不是同花
bool flag2 = true;
int color = card[0].color;
for (int i = 1; i < 5; i++)
{
if (card[i].color != color)
{
flag2 = false;
break;
}
}
//如果是同花
if (flag2)
{
if (card[4].score == 14)
ans = "ROYAL FLUSH";
else
ans = "STRAIGHT FLUSH";
}
//如果不是同花
else
{
ans = "STRAIGHT";
}
}
//考虑是同花? 四条还是葫芦
else
{
bool flag2 = true;
int color = card[0].color;
for (int i = 1; i < 5; i++)
{
if (card[i].color != color)
{
flag2 = false;
break;
}
}
if (flag2)
ans = "FLUSH";
for (int i = 0; i < 5; i++)
{
cnt[card[i].score]++;
}
sort(cnt, cnt + 16);
if (cnt[15] == 4)
ans = "FOUR OF A KIND";
else if (cnt[15] == 3 && cnt[14] == 2)
ans = "FULL HOUSE";
}
if (ans.empty())
cout << "FOLD";
else
cout << ans;
return 0;
}
106 订单编号
看题解后学到了利用set维护集合的办法
思路就是题解中的两种情况的不同处理办法
第一是在当前集合中可以容纳该订单编号 那么将集合划分为两个集合
第二是在当前集合不可以容纳该订单编号 那么就找下一个集合 取左边界并更新集合边界
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
int a[maxn];
set<pair<int, int>> st; // 维护未被使用的编号,set里面的每个区间的所有编号都是未被使用的
void insert(int l, int r) {
if (l > r) return;
st.insert({ r,l });
}
int main()
{
int n; cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
st.insert(make_pair(2e9, 1));
for (int i = 1; i <= n; ++i) {
auto it = st.upper_bound({ a[i],0 });
int l = it->second, r = it->first;
if (a[i] > l) {
cout << a[i] << " ";
st.erase(it);
insert(l, a[i] - 1);
insert(a[i] + 1, r);
}
else {
cout << l << " ";
st.erase(it);
insert(l + 1, r);
}
}
return 0;
}
202 路径计数
很简单的DP 不多说 对于障碍来说 只要像vis数组一样 判一下能不能通过 如果不能通过就始终赋0即可
#include<bits/stdc++.h>
using namespace std;
const int Mode = 1e9 + 7;
int n;
const int maxn = 100 + 5;
bool vis[maxn][maxn];
int dp[maxn][maxn];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> vis[i][j];
dp[1][1] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (vis[i][j])
dp[i][j] += (dp[i - 1][j] + dp[i][j - 1])%Mode;
}
}
cout << dp[n][n];
return 0;
}
203 最大和上升子序列
与之前的week7中做过的最长上升子序列在思路和代码上基本保持一致
具体就在代码里谈最大和上升子序列和最长上升子序列的区别啦
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
//最大和上升子序列问题 值得关注的是子序列这一概念
//子序列所指的是从一段序列中 任取不连续的部分所组成的新序列
//其关键在于不连续的取 这也就是状态转移时的思想之一
//我们研究对于第n个数字的最大子序列
//由于最大子序列是从1~n这前n个数字去选取的
//我们的状态转移方程就是
// if (num[i] > num[j])
//{
// res[i] = max(res[i], res[j] + num[i]);
//}
//遍历从1~n这n个数 只要第n个数大于前面的某一个数m时 就可以考虑是保持
//当前n的子序列 还是选择更新至m的子序列 并增加上n本身的值
//同时 还有一个完全一致的题目是
//最大上升子序列
//其更改的部分只有
//res[i] = max(res[i], res[j] + 1);
//res数组表示的意思从前n个最大上升子序列的和 变为 最大上升子序列的个数
int n;
int num[maxn];
//用res去存储一段子序列的长度
int res[maxn];
int ans[maxn];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> num[i];
res[i] = num[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i - 1; j++)
{
if (num[i] > num[j])
{
res[i] = max(res[i], res[j] + num[i]);
}
}
}
sort(res + 1, res + 1 + n);
printf("%d", res[n]);
return 0;
}