A - 必做题11-1 T1065
题意:
蒜头君从现在开始工作,年薪 N 万。他希望在蒜厂附近买一套 60 平米的房子,现在价格是 200 万。假设房子价格以每年百分之 K 增长,并且蒜头君未来年薪不变,且不吃不喝,不用交税,每年所得 N 万全都积攒起来,问第几年能够买下这套房子?(第一年年薪 N万,房价 200 万)
输入格式
一行,包含两个正整数 N(10 \le N \le 50)N(10≤N≤50),K(1 \le K \le 20)K(1≤K≤20),中间用单个空格隔开。
输出格式
如果在第 2020 年或者之前就能买下这套房子,则输出一个整数 M,表示最早需要在第 M 年能买下;否则输出"Impossible"。
样例输入
50 10
样例输出
8
思路:
用两个sum来分别代表蒜头君的积蓄和房子价格(即sum1=N,sum2=200)
然后以年数增加的方式进行循环:每年积蓄+N,房子价格=value*(1+K/100),每增加一次判断积蓄是否大于等于房子价格并且积攒年数是否小于等于20,若是的话输出年数。累计循环到年数>=20 如果不能的话输出-1。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
double N, K;
double sum1, sum2;
sum2 = 200;
int year = 1;
cin >> N >> K; //年薪和房价增长率
sum1 = N;
while (1)
{
sum1 += N; //积蓄
sum2 = sum2 * (1 + K / 100); //房价
year++;
if (sum1 >= sum2 && year <= 20) //比较
{
cout << year << endl;
break;
}
if (year >= 20) //太穷了
{
cout << "Impossible" << endl;
break;
}
}
return 0;
}
B - 必做题11-2 T3176
蒜头君的班级里有 n^2
个同学,现在全班同学已经排列成一个 n * n的方阵,但是老师却临时给出了一组新的列队方案
为了方便列队,所以老师只关注这个方阵中同学的性别,不看具体的人是谁
这里我们用 0 表示男生,用 1 表示女生
现在蒜头君告诉你同学们已经排好的方阵是什么样的,再告诉你老师希望的方阵是什么样的
他想知道同学们已经列好的方阵能否通过顺时针旋转变成老师希望的方阵
不需要旋转则输出 0
顺时针旋转 90° 则输出 1
顺时针旋转 180° 则输出 2
顺时针旋转 270° 则输出 3
若不满足以上四种情况则输出 -1
若满足多种情况,则输出较小的数字
输入格式
第一行为一个整数 n
接下来的 n行同学们已经列好的 01 方阵;
再接下来的 nn行表示老师希望的的 01 方阵。
输出格式
输出仅有一行,该行只有一个整数,如题所示。
数据范围
对于 100%100% 的数据中,1 \leq n \leq 201≤n≤20
样例输入
4
0 0 0 0
0 0 0 0
0 1 0 0
0 0 0 0
0 0 0 0
0 1 0 0
0 0 0 0
0 0 0 0
样例输出
1
思路:
1.方阵使用二维数组 矩阵的方式来存储
2.旋转180度=旋转90度+旋转90度。同理旋转270度为三次旋转90度(均为顺时针)只需写出旋转90的变换方法即可。
3.旋转90度 列变成行,n-行数+1变成列。
4.依次从90 180 270判断,降低复杂度。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
bool d=false;
int a[21][21], b[21][21],c[21][21];
void rotate() //将a矩阵顺旋转90度 ,列变成行 n-i+1变成列
{
for (int i = n; i >= 1; i--)
for (int j = 1; j <= n; j++)
{
c[j][i] = a[n - i + 1][j];
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j] = c[i][j];
}
bool judge() //判断转换后的矩阵是否与原矩阵相等
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
if (a[i][j] != b[i][j])
return false;
}
return true;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
cin >> b[i][j];
if (judge())
{
cout << 0 << endl;
d = true;
}
if (!d)
{
rotate();
if (judge())
{
cout << 1 << endl;
d = true;
}
}
if (!d)
{
rotate();
if (judge())
{
cout << 2 << endl;
d = true;
}
}
if (!d)
{
rotate();
if (judge())
{
cout << 3 << endl;
d = true;
}
}
if (!d)
{
cout << -1 << endl;
}
return 0;
}
C - 必做题11-3 T1111
ulius Caesar 曾经使用过一种很简单的密码。对于明文中的每个字符,将它用它字母表中后 5 位对应的字符来代替,这样就得到了密文。比如字符’A’用’F’来代替。如下是密文和明文中字符的对应关系。
密文{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}
明文{V W X Y Z A B C D E F G H I J K L M N O P Q R S T U}
你的任务是对给定的密文进行解密得到明文。
你需要注意的是,密文中出现的字母都是大写字母。密文中也包括非字母的字符,对这些字符不用进行解码。
输入格式
一行,给出密文,密文不为空,而且其中的字符数不超过 200。
输出格式
输出一行,即密文对应的明文。
**样例输入**
NS BFW, JAJSYX TK NRUTWYFSHJ FWJ YMJ WJXZQY TK YWNANFQ HFZXJX
**样例输出**
IN WAR, EVENTS OF IMPORTANCE ARE THE RESULT OF TRIVIAL CAUSES
思路:
利用ACCII码,对于字符A-E之间的密文,让其对应的ASCII值直接加上21。
对于F字符-Z之间的密文,让其对应的ASCII值直接减去5。重新输出即可得到正确答案。
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
string s;
getline(cin, s);
for (int i = 0; i < s.size(); i++)
{
if (s[i] >= 65 && s[i] <= 90)
{
if (s[i] >= 65 && s[i] <= 69)
s[i] += 21;
else
s[i] -= 5;
}
}
for (int i = 0; i < s.size(); i++)
{
cout << s[i];
}
return 0;
}
D - 必做题11-4 codeforces-1138A
东东和他的女朋友(幻想的)去寿司店吃晚餐(在梦中),他发现了一个有趣的事情,这家餐厅提供的 n 个的寿司被连续的放置在桌子上 (有序),东东可以选择一段连续的寿司来吃
东东想吃鳗鱼,但是东妹想吃金枪鱼。核 平 起 见,他们想选择一段连续的寿司(这段寿司必须满足金枪鱼的数量等于鳗鱼的数量,且前一半全是一种,后一半全是另外一种)我们用1代表鳗鱼,2代表金枪鱼。
比如,[2,2,2,1,1,1]这段序列是合法的,[1,2,1,2,1,2]是非法的。因为它不满足第二个要求。
东东希望你能帮助他找到最长的一段合法寿司,以便自己能吃饱。
Input
输入:
第一行:一个整数n(2≤n≤100000),寿司序列的长度。
第二行:n个整数(每个整数不是1就是2,意义如上所述)
Output
输出:一个整数(代表东东可以选择的最长的一段连续的且合法的寿司)
Examples
Input
7
2 2 2 1 1 2 2
Output
4
Input
6
1 2 1 2 1 2
Output
2
Input
9
2 2 1 1 1 2 2 2 2
Output
6
思路:
利用两个vector来分别存储1 2。从整个序列开始遍历,将最开始的一种存进v[0]中,直到遇到不同种,遇到不同种的时候计算出存进v[0]的size。然后清空容器,然后两个容器循环使用 用另外一个容器存入另外一种。每一次循环都先找到两个size的最小值*2与前一个最大值比较最后找到最大值就行了。
注意:进行遍历之后还要进行一次比较。否则最后存储v的size没比较上。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int n,j=0;
int a[100001];
vector<int> v[2];
int cnt[2] = {0};
int maxl=0;
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
v[j].push_back(a[0]);
for (int i = 1; i < n; i++)
{
if (a[i] == a[i - 1])
{
v[j].push_back(a[j]);
}
else
{
cnt[j] = v[j].size();
v[j].clear();
if (j == 0) j++;
else j--;
v[j].push_back(a[i]);
}
maxl =max(maxl,2* min(cnt[0], cnt[1]));
}
cnt[j] = v[j].size();
maxl = max(maxl, 2 * min(cnt[0], cnt[1]));
cout << maxl << endl;
}
E - 选做题11-1 东东与 ATM PQJ-1276
题意:
一家银行计划安装一台用于提取现金的机器。
机器能够按要求的现金量发送适当的账单。
机器使用正好N种不同的面额钞票,例如D_k,k = 1,2,…,N,并且对于每种面额D_k,机器都有n_k张钞票。
例如,
N = 3,
n_1 = 10,D_1 = 100,
n_2 = 4,D_2 = 50,
n_3 = 5,D_3 = 10
表示机器有10张面额为100的钞票、4张面额为50的钞票、5张面额为10的钞票。
东东在写一个 ATM 的程序,可根据具体金额请求机器交付现金。
注意,这个程序计算程序得出的最大现金少于或等于可以根据设备的可用票据供应有效交付的现金。
Input
程序输入来自标准输入。 输入中的每个数据集代表特定交易,其格式为:Cash N n1 D1 n2 D2 … nN DN其中0 <= Cash <= 100000是所请求的现金量,0 <= N <= 10是 纸币面额的数量,0 <= nk <= 1000是Dk面额的可用纸币的数量,1 <= Dk <= 1000,k = 1,N。 输入中的数字之间可以自由出现空格。 输入数据正确。
Output
对于每组数据,程序将在下一行中将结果打印到单独一行上的标准输出中。
Sample Input
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10
Sample Output
735
630
0
0
思路:
分析题目,这是一道多重背包问题,通过二进制拆分就变成了0-1背包
1.二进制拆分原理:
一定可以表达一系列连续的正数,下面用例子证明 ,把22进行二进制拆分: 成为1,2,4,8,7;由1,2,4,8可以组成1--15之间所有的数,而对于16--22之间的数,可以先减去剩余的7,那就是1--15之间的数可以用1,2,4,8表示了。
2.将拆分出来的数字乘以纸币面值,然后加入新的背包序列,就变成了0/1背包问题。
3. 0/1背包问题可以通过滚动数组(逆序)的方法存储。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int cash, n, cnt = 0;
int c[11], v[11],w[11];
int vv[1001], ww[1001];
int d[1000000];
int main()
{
while (cin >> cash>>n)
{
for (int i = 1; i <= n; i++)
{
cin >> c[i] >> v[i];
w[i] = v[i];
}
cnt = 0;
for (int i = 1; i <= n; i++)
{
int a = c[i], j = 1;
for (; j <= a; j = j * 2)
{
cnt++;
vv[cnt] = j * v[i];
ww[cnt] = j * w[i];
a = a - j;
}
if (a > 0)
{
cnt++;
vv[cnt] = a * v[i];
ww[cnt] = a * w[i];
}
}
memset(d, 0, sizeof(d));
for (int i = 1; i <= cnt; i++)
{
for (int j = cash; j >= 0; j--)
{
if (j - ww[i] >= 0)
{
d[j] = max(d[j], d[j - ww[i]] + vv[i]);
}
}
}
cout << d[cash] << endl;
}
return 0;
}
F - 选做题11-2 东东开车了
题意:
东东开车出去泡妞(在梦中),车内提供了 n 张CD唱片,已知东东开车的时间是 n 分钟,他该如何去选择唱片去消磨这无聊的时间呢
假设:
CD数量不超过20张
没有一张CD唱片超过 N 分钟
每张唱片只能听一次
唱片的播放长度为整数
N 也是整数
我们需要找到最能消磨时间的唱片数量,并按使用顺序输出答案(必须是听完唱片,不能有唱片没听完却到了下车时间的情况发生)
本题是 Special Judge
Input
多组输入
每行输入第一个数字N, 代表总时间,第二个数字 M 代表有 M 张唱片,后面紧跟 M 个数字,代表每张唱片的时长 例如样例一: N=5, M=3, 第一张唱片为 1 分钟, 第二张唱片 3 分钟, 第三张 4 分钟
所有数据均满足以下条件:
N≤10000
M≤20
Output
输出所有唱片的时长和总时长,具体输出格式见样例
**Sample Input**
5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2
**Sample Output**
1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45
思路:
这是一道01背包,但是要输出路径,所以需要中间的过程。利用滚动数组计算01背包,在计算的同时记录该元素是否被加入路径中。当d[j] = d[j - w[i]] + v[i]时。当前元素被选定,将v[i]插入到vector中。找到最终路径时,要从后向前找路径中的元素。
代码:
#include <iostream>
#include <vector>
#include<algorithm>
#include<cstring>
using namespace std;
int n, m;
int v[21], w[21];
int d[10100];
vector<int> vv[10100];
int main()
{
while (cin >> n>> m)
{
for (int i = 1; i <= m; i++)
{
cin >> v[i];
w[i] = v[i];
}
memset(d, 0, sizeof(d));
for (int i = 1; i <= n; i++)
vv[i].clear();
for (int i = 1; i <= m; i++)
{
for (int j = n; j >= 0; j--)
{
if (j - w[i] >= 0)
{
if (d[j] < d[j - w[i]] + v[i])
{
d[j] = d[j - w[i]] + v[i];
vv[j] = vv[j - w[i]];
vv[j].push_back(v[i]);
}
}
}
}
for (int i = 0; i < vv[n].size(); i++)
cout << vv[n][i] << " ";
cout << "sum:" << d[n] << endl;
}
}