week9作业
A-咕咕东的目录管理器
题目大意
命令 | 类型 | 实现 | 说明 |
---|---|---|---|
MKDIR s | 操作 | 在当前目录下创建一个子目录 s,s 是一个字符串 | 创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR” |
RM s | 操作 | 在当前目录下删除子目录 s,s 是一个字符串 | 删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR” |
CD s | 操作 | 进入一个子目录 s,s 是一个字符串(执行后,当前目录可能会改变) | 进入成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”。特殊地,若 s 等于 “…” 则表示返回上级目录,同理,返回成功输出 “OK”,返回失败(当前目录已是根目录没有上级目录)则输出 “ERR” |
SZ | 询问 | 输出当前目录的大小 | 也即输出 1+当前目录的子目录数 |
LS | 询问 | 输出多行表示当前目录的 “直接子目录” 名 | 若没有子目录,则输出 “EMPTY”;若子目录数属于 [1,10] 则全部输出;若子目录数大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。 |
TREE | 询问 | 输出多行表示以当前目录为根的子树的前序遍历结果 | 若没有后代目录,则输出 “EMPTY”;若后代目录数+1(当前目录)属于 [1,10] 则全部输出;若后代目录数+1(当前目录)大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个 |
UNDO | 特殊 | 撤销操作 | 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR” |
题解
模拟
首先可以看出这个目录管理器是个树状结构,然后我们考虑对于树中的每个节点都需要维护哪些值,首先我们肯定需要维护当前点的名字。
MKDIR命令,实际上就是给某个节点加一个儿子节点,对于儿子节点我们开一个map映射,将儿子节点的名字与编号对应起来。
RM命令,删除儿子节点,直接将该节点从map中移除即可
CD命令,进入一个子目录或者回到上一级目录,因为需要回到上一级目录所以我们还需要记录每个节点父亲的编号
SZ命令,每次都遍历统计太慢了,我们对于每个节点维护一个子树大小的值sz,需要注意的是每次插入和删除儿子都要更新当前点到根路径上所有节点的sz值。
LS命令,直接根据map即可得到答案。
TREE命令,这个命令牵扯到遍历,但是每次都遍历的代价是很高的,通过观察数据的规模我们发现节点数远少于TREE操作数,所以对于很多询问我们遍历的树结构是相同的,所以可以打一个lazy标记,就是我们存储每次遍历的答案,只有两次TREE操作之间存在更新操作时,才重新进行遍历,这个标记的维护可以跟sz一起,sz改变,则标记被激活。另外在遍历的时候也不需要全部遍历,只遍历我们需要的数量即可。
UNDO命令,撤销操作,只有MKDIR、RM、CD操作存在撤销,所以对于所有成功执行的这三类命令,我们用vector进行记录,然后根据记录进行操作即可。
#include<bits/stdc++.h>
using namespace std;
string ch[10]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
int n,now,cnt;
struct data{
string name;
map<string,int> mp;
vector<string> pre,bk;
int fa,sz;
bool pd;
void clear(){
mp.clear();
pre.clear();
bk.clear();
fa=0; sz=1;
pd=false;
}
void ls()
{
int m=mp.size();
if (!m) {
printf("EMPTY\n");
return;
}
if(m<=10) {
for(map<string,int>::iterator it=mp.begin();it!=mp.end();it++)
cout<<it->first<<endl;
return;
}
int t=1;
map<string,int>::iterator it=mp.begin();
for(;t<=5;it++)
cout<<it->first<<endl,t++;
cout<<"...\n";
t=1;
it=mp.end();
for(int i=1;i<=5;i++) it--;
for(;it!=mp.end();it++)
cout<<it->first<<endl,t++;
}
void sz_print()
{
printf("%d\n",sz);
}
}a[100003];
struct command{
int opt,pr;
string s;
command(int _opt,string _s,int _pr=0){
opt=_opt; s=_s; pr=_pr;
}
};
vector<command> v;
int KK;
void preOrder(int num,int x,vector<string> &u)
{
u.push_back(a[x].name); num--;
if (!num) return;
int temp=num;
for (map<string,int>::iterator it=a[x].mp.begin();it!=a[x].mp.end();it++){
int k=it->second;
preOrder(min(a[k].sz,temp),k,u);
temp-=a[k].sz;
if (temp<=0) return;
}
}
void lastOrder(int num,int x,vector<string> &u)
{
int temp=num;
map<string,int>::iterator it=a[x].mp.end();
int nn=a[x].mp.size();
for (;nn--;) {
it--;
int k=it->second;
lastOrder(min(a[k].sz,temp),k,u);
temp-=a[k].sz;
if (temp<=0) return;
}
u.push_back(a[x].name);
}
void update(int x,int num)
{
while (x) {
a[x].pd=false;
a[x].sz+=num;
x=a[x].fa;
}
}
void pushdown(int x)
{
a[x].pre.clear();
a[x].bk.clear();
if (a[x].sz>10) preOrder(5,x,a[x].pre),lastOrder(5,x,a[x].bk);
else preOrder(a[x].sz,x,a[x].pre);
a[x].pd=true;
}
bool mkdir(string s,int pr=0)
{
if (a[now].mp.find(s)!=a[now].mp.end()) return false;
int t=pr;
if(!pr){
a[++cnt].clear();
a[cnt].name=s; a[cnt].fa=now;
t=cnt;
}
a[now].mp[s]=t;
update(now,a[t].sz);
return true;
}
bool rm(string s,int pr=0)
{
map<string,int>::iterator it=a[now].mp.find(s);
if(it==a[now].mp.end()) return false;
update(now,-a[it->second].sz);
KK=it->second;
a[now].mp.erase(it);
return true;
}
void cd(string s)
{
int t=now;
if (s=="..") {
if(a[now].fa) now=a[now].fa;
else {
printf("ERR\n");
return;
}
}
else {
map<string,int>::iterator it=a[now].mp.find(s);
if(it==a[now].mp.end()) {
printf("ERR\n");
return;
}
now=it->second;
}
v.push_back(command(2,s,t));
printf("OK\n");
}
void tree()
{
if (!a[now].pd) pushdown(now);
if(a[now].sz==1) {
printf("EMPTY\n");
return;
}
if(a[now].sz<=10) {
for(int i=0;i<a[now].pre.size();i++)
cout<<a[now].pre[i]<<endl;
return;
}
for (int i=0;i<5;i++) cout<<a[now].pre[i]<<endl;
cout<<"...\n";
int t=a[now].bk.size();
for (int i=1;i<=5;i++) cout<<a[now].bk[t-i]<<endl;
}
void undo()
{
if(v.size()==0) {
printf("ERR\n");
return;
}
command temp=v.back();
v.pop_back();
bool flag=false;
//cout<<temp.s<<" "<<temp.pr<<endl;
switch(temp.opt) {
case 0:flag=rm(temp.s,temp.pr); break;
case 1:flag=mkdir(temp.s,temp.pr); break;
case 2:now=temp.pr; flag=true; break;
}
if (flag) printf("OK\n");
else printf("ERR\n");
}
int main()
{
freopen("a.in","r",stdin);
int T; scanf("%d",&T);
while (T--) {
scanf("%d",&n); now=cnt=1;
v.clear();
a[now].clear(); a[now].name="root";
for (int k=1;k<=n;k++) {
int i;
string s,name;
cin>>s;
for (i=0;i<7;i++)
if(s==ch[i]) break;
if (i<3) cin>>name;
switch(i) {
case 0: if (mkdir(name)) v.push_back(command(0,name,cnt)),printf("OK\n");
else printf("ERR\n");
break;
case 1:if (rm(name)) v.push_back(command(1,name,KK)),printf("OK\n");
else printf("ERR\n");
break;
case 2:cd(name); break;
case 3:a[now].sz_print(); break;
case 4:a[now].ls(); break;
case 5:tree(); break;
case 6:undo(); break;
}
}
cout<<endl;
}
return 0;
}
week10作业
C-拿数问题II
题目大意
给一个序列,里边有 n n n 个数,每一步能拿走一个数,比如拿第 i i i 个数, A i = x Ai = x Ai=x,得到相应的分数 x x x,但拿掉这个 A i Ai Ai后, x + 1 x+1 x+1 和 x − 1 x-1 x−1 (如果有 A j = x + 1 Aj = x+1 Aj=x+1 或 A j = x − 1 Aj = x-1 Aj=x−1 存在) 就会变得不可拿(但是有 A j = x Aj = x Aj=x 的话可以继续拿这个 x x x)。求最大分数。
题解
动态规划
这个题我们对序列进行一下处理就可以变成经典的拿数问题。
对于每个数值x,我们记录他出现的次数a[x]和x的最大值max
那么我们枚举
2
−
m
a
x
2-max
2−max之间的所有数i (特别的
d
p
[
1
]
=
a
[
1
]
dp[1]=a[1]
dp[1]=a[1])
对于出现次数不为0的数,状态转移方程为
d
p
[
i
]
=
m
a
x
(
d
p
[
i
−
1
]
,
d
p
[
i
−
2
]
+
i
∗
a
[
i
]
)
dp[i]=max(dp[i-1],dp[i-2]+i*a[i])
dp[i]=max(dp[i−1],dp[i−2]+i∗a[i])
出现次数为0的数
d
p
[
i
]
=
d
p
[
i
−
1
]
dp[i]=dp[i-1]
dp[i]=dp[i−1]
#include<iostream>
#include<cstdio>
#define N 100003
#define LL long long
using namespace std;
LL dp[N],a[N];
int main()
{
int n,mx=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
int x; scanf("%d",&x);
a[x]++;
mx=max(mx,x);
}
dp[1]=a[1];
for (int i=2;i<=mx;i++)
if (a[i]) {
dp[i]=max(dp[i-1],dp[i-2]+(LL)i*a[i]);
}
else dp[i]=dp[i-1];
printf("%lld\n",dp[mx]);
return 0;
}
week10限时模拟
B-东东转魔方
题目大意
东东有一个二阶魔方,即2×2×2的一个立方体组。立方体由八个角组成。
魔方的每一块都用三维坐标(h, k, l)标记,其中h, k, l∈{0,1}。六个面的每一个都有四个小面,每个小面都有一个正整数。
对于每一步,东东可以选择一个特定的面,并把此面顺时针或逆时针转90度。
请你判断,是否东东可以在一个步骤还原这个魔方(每个面没有异色)。
题解
模拟
我们对于魔方的每个小块给他一个编号1-24
每次旋转会影响8个小块,然后我们可以按照他们的顺时针的顺序将六个面的旋转会影响的小块编号罗列出来,其实就是程序中的数组v
预处理好数组v后问题就简单了,假设我们转动第i面,每次旋转无非就是八个小块交换位置
v
[
i
]
[
j
]
−
>
v
[
i
]
[
(
j
+
2
)
v[i][j]->v[i][(j+2)
v[i][j]−>v[i][(j+2)%
8
]
8]
8]或者
v
[
i
]
[
j
]
−
>
v
[
i
]
[
(
j
+
6
)
v[i][j]->v[i][(j+6)
v[i][j]−>v[i][(j+6)%
8
]
8]
8]
#include<iostream>
#include<cstdio>
using namespace std;
int a[30],b[30];
int v[6][8]={{18,20,5,6,23,21,16,15},{3,4,23,24,10,9,19,20},{1,3,5,7,9,11,13,15},
{2,4,6,8,10,12,14,16},{7,8,24,22,14,13,17,19},{1,2,21,22,12,11,17,18}};
bool check(int *c)
{
for (int i=1;i<=24;i+=4) {
int t=c[i];
for (int j=i+1;j<i+4;j++)
if (t!=c[j]) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
for (int i=1;i<=24;i++) scanf("%d",&a[i]);
if(check(a)) {
printf("YES\n");
continue;
}
bool pd=false;
for (int t=0;t<6;t++) {
for (int i=1;i<=24;i++) b[i]=a[i];
for (int i=0;i<8;i++) b[v[t][i]]=a[v[t][(i+2)%8]];
if (check(b)) {
printf("YES\n");
pd=true;
break;
}
for (int i=0;i<8;i++) b[v[t][i]]=a[v[t][(i+6)%8]];
if (check(b)) {
printf("YES\n");
pd=true;
break;
}
}
if (!pd) printf("NO\n");
}
}
月模拟题
CSP201609-3 炉石传说
题目大意
- 玩家会控制一些角色,每个角色有自己的生命值和攻击力。当生命值小于等于 0 时,该角色死亡。角色分为英雄和随从。
* 玩家各控制一个英雄,游戏开始时,英雄的生命值为 30,攻击力为 0。当英雄死亡时,游戏结束,英雄未死亡的一方获胜。
* 玩家可在游戏过程中召唤随从。棋盘上每方都有 7 个可用于放置随从的空位,从左到右一字排开,被称为战场。当随从死亡时,它将被从战场上移除。
* 游戏开始后,两位玩家轮流进行操作,每个玩家的连续一组操作称为一个回合。
* 每个回合中,当前玩家可进行零个或者多个以下操作:
1) 召唤随从:玩家召唤一个随从进入战场,随从具有指定的生命值和攻击力。
2) 随从攻击:玩家控制自己的某个随从攻击对手的英雄或者某个随从。
3) 结束回合:玩家声明自己的当前回合结束,游戏将进入对手的回合。该操作一定是一个回合的最后一个操作。
* 当随从攻击时,攻击方和被攻击方会同时对彼此造成等同于自己攻击力的伤害。受到伤害的角色的生命值将会减少,数值等同于受到的伤害。例如,随从 X 的生命值为 HX、攻击力为 AX,随从 Y 的生命值为 HY、攻击力为 AY,如果随从 X 攻击随从 Y,则攻击发生后随从 X 的生命值变为 HX - AY,随从 Y 的生命值变为 HY - AX。攻击发生后,角色的生命值可以为负数。
本题将给出一个游戏的过程,要求编写程序模拟该游戏过程并输出最后的局面。
题解
模拟
这类游戏的模拟题最重要的就是在写之前先把类规划定义好,然后按照给出的要求实现其操作即可。不过这道题操作和需要维护的值都比较简单,直接用数组也可以。
#include<bits/stdc++.h>
using namespace std;
struct summo{
int attack,health;
summo(int _a=0,int _h=0) {
attack=_a; health=_h;
}
};
struct hero{
summo s[8];
int opt,cnt;
hero(int _opt=0){
opt=_opt;
cnt=0;
s[0].health=30;
s[0].attack=0;
}
void call_summon(int pos,int ak,int hl)
{
for (int i=cnt;i>=pos;i--) s[i+1]=s[i];
cnt++;
s[pos].attack=ak;
s[pos].health=hl;
}
void clear(int x){
if(x==0) return;
for(int i=x;i<cnt;i++) s[i]=s[i+1];
cnt--;
}
void print(){
printf("%d\n",s[0].health);
printf("%d ",cnt);
for (int i=1;i<=cnt;i++) printf("%d ",s[i].health);
printf("\n");
}
}player[2];
void attack(int opt,int aker,int dfer)
{
player[opt].s[aker].health-=player[opt^1].s[dfer].attack;
player[opt^1].s[dfer].health-=player[opt].s[aker].attack;
if (player[opt].s[aker].health<=0) player[opt].clear(aker);
if (player[opt^1].s[dfer].health<=0) player[opt^1].clear(dfer);
}
int main()
{
freopen("a.in","r",stdin);
int T;
scanf("%d",&T);
int opt=0;
for (int i=0;i<2;i++) player[i]=hero(i);
while(T--) {
string s;
cin>>s;
if(s=="summon") {
int pos,ak,hl;
scanf("%d%d%d",&pos,&ak,&hl);
player[opt].call_summon(pos,ak,hl);
}
if(s=="attack") {
int ak,df;
scanf("%d%d",&ak,&df);
attack(opt,ak,df);
}
if(s=="end") {
opt=opt^1;
}
}
if(player[0].s[0].health>0&&player[1].s[0].health>0) printf("0\n");
if(player[0].s[0].health>0&&player[1].s[0].health<=0) printf("1\n");
if(player[0].s[0].health<=0&&player[1].s[0].health>0) printf("-1\n");
player[0].print();
player[1].print();
return 0;
}