洛谷11月月赛总结

NOIP前夕,我为了最后冲刺,到处找题做,于是便发现了洛谷,于是去找题做。
洛谷11月的这次月赛比较简单,虽说是提高组难度,但我在3个小时内且不加检查的情况下做到了129分,而事后再加以检查就变成了209分。。好吧,废话不多说,下次要小心看题,直接先上一点题解:

洛谷2614 计算器弹琴

本题地址: http://www.luogu.org/problem/show?pid=2614

题目描述

众所周知,计算器可以拿来干很多它本不应该干的事情,比如写作文。(参看洛谷P2549)
小A发现了一个计算器的另一个隐藏功能——弹琴。
http://www.bilibili.com/video/av2205500/

如果按上一个键,比如说1,就会发出中音“Do”。
这边给出按键音高表
+ 低音Fa
< 低音So
* 低音La
/ 低音Xi
1 中音Do
2 中音Re
3 中音Mi
4 中音Fa
5 中音So
6 中音La
7 高音Xi
8 高音Do
9 高音Re
= 高音Mi
% 高音Fa
C 高音So
M 高音La

现在小A搞到了一份乐谱——我们称为计算器谱,一种变形的简谱。
时值(也就是按的时间长度)是这么记录的,例如:
1 是四分音符,占1拍。
1- 是二分音符,占2拍。
1--- 是全音符,占四拍。
对于小于四分音符的音符,我们用嵌套括号表示,例如
(1(34(56))2)
1和2在一层括号中,是八分音符,占0.5拍。
3和4在两层括号中,是16分音符,占0.25拍。
5和6在三层括号中,是32分音符,占1/8拍。当然实际上比较少见。
括号中不会出现‘-’这个符号。
不会出现四层或以上的括号。
在一个音符后面添加一个附点即“.”表示这个音符延长1/2倍。
例如
1-.是3拍,1.是1.5拍,(3.(45.))3是3/4拍,4是1/4拍,5是3/8拍。
附点不会连续添加两个或以上,也不会出现超过四拍的音符。
不考虑其他的乐理符号。
另外整个乐谱会给一个速度,整数,意思是一分钟多少拍。

为了美观,乐谱可以随便换行、添加空格。这个忽略即可。
现在小A想知道,按完这个谱子,需要多少时间(单位:秒)

输入输出格式

输入格式:

第一行,两个整数n,T,表示谱子行数以及速度(拍每分)
接下来n行,给出乐谱。

输出格式:

一个整数,表示演奏需要花费的时间,单位秒,舍去小数部分。

输入输出样例

输入样例#1:
2 60
3345 5432 1123 322-
3345 5432 1123 211-
输出样例#1:
32
(一共32拍,每分钟60拍,所以是32秒。对了,这是欢乐颂的开头部分)
输入样例#2:
5 120
3(1.(3))55 8(7.(6))65 655(3.(1)) (4.(4))32- 
3(1.(2))35 8(7.(6))65 655(4.(3)) (2.(3))21- 
2.(3)44 6(6.(6))(5.(4))3 3.(5)88 (9.(8)7.(6))5-
=.(=)(9.(8))7 9.(8)(7.(6))5  8(856543) (2.(3))43- 
=.(=)(9.(8))7 9.(8)(7.(6))5  8(857654) (3.(4))21- 
输出样例#2:
40
(一共80拍,别问我怎么数的,一分钟120拍的话,是40秒。至于这是什么曲子?根据相关的法律政策,该部分未予显示。)

说明

http://bd.kuwo.cn/yinyue/4641527

对于40%的数据,没有附点没有括号
对于100%的数据,括号层数不会超过3层,不超过100行,每行不超过100个字符。
对于其中的一个数据,是《千本樱》。

【题目分析】
这道题显然是一道模拟题,但是比较长,要仔细读,我当初就是因为没有仔细读,所以被坑走了80分。我当时没有看到1---后面有3个减号,所以就直接乘了二,下次一定要引以为戒。

【算法分析】
忽略掉回车和空格,直接读就行了,只不过整体的程序比较繁琐,要多想几下,分成多个函数来编程,理解好题意,别错就行。

【代码】

# include <iostream>
# include <cstring>
# include <cstdio>
# include <string>
# include <cmath>
using namespace std;
const int MAXN = 10001;
int N,T;
int stacksize;
int pos = 0;
int k;
double MusicLen[MAXN];
double Ans;
string str;

void Init(){
k = 0;
stacksize = 0;
str.clear();
Ans = 0.0;
for (int i=1;i!=MAXN;++i) MusicLen[i] = 0.0;
MusicLen[0] = 1;
}

void Read(){
scanf("%d%d",&N,&T);
char c = getchar();
while (c != EOF){
if (c != ' ' && c != '\n') str.push_back(c);
c = getchar();
}
}

void Cut(){
int len = str.size();
for (int i=1;i!=len;++i){
switch(str[i]){
case '-':
++k;
break;
case '.':
MusicLen[pos] *= 1.5;
break;
case '(':
++stacksize;
break;
case ')':
--stacksize;
break;
default:
if (k != 0) MusicLen[pos] *= k+1;
k = 0;
++pos;
MusicLen[pos] = pow(0.5,stacksize);
}
}
}

void Sum(){
for (int i=0;i<=pos;++i){
// printf("%f\n",MusicLen[i]);
Ans += MusicLen[i];
}
}

int main(){
Init();
Read();
Cut();
if (k != 0) MusicLen[pos] *= k+1;
Sum();
// printf("%f\n",Ans);
cout << floor(Ans * 60.0 / (double)T) << endl;
return 0;
}


洛谷2622 关灯问题II

本题地址: http://www.luogu.org/problem/show?pid=2622

题目描述

现有n盏灯,以及m个按钮。每个按钮可以同时控制这n盏灯——按下了第i个按钮,对于所有的灯都有一个效果。按下i按钮对于第j盏灯,是下面3中效果之一:如果a[i][j]为1,那么当这盏灯开了的时候,把它关上,否则不管;如果为-1的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是0,无论这灯是否开,都不管。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。

输入输出格式

输入格式:

前两行两个数,n m
接下来m行,每行n个数,a[i][j]表示第i个开关对第j个灯的效果。

输出格式:

一个整数,表示最少按按钮次数。如果没有任何办法使其全部关闭,输出-1

输入输出样例

输入样例#1:
3
2
1 0 1
-1 1 0
输出样例#1:
2

说明

对于20%数据,输出无解可以得分。
对于20%数据,n<=5
对于20%数据,m<=20
上面的数据点可能会重叠。
对于100%数据 n<=10,m<=100

【题目分析】
题目中说到了“最少用多少次”,所以思路就很清晰了,建图,然后用最短路!考虑到这样的数据,怎样的最短路都很简单,但是为了方便,我就试着用了一下SPFA,因为一盏灯对应一个操作,所以说就相当于是一条边,这个图相对比较稠密,所以应该用比较高效的算法,而且写的还得比较简洁。鉴于数据规模,可以什么优化都不用~

【算法分析】
SPFA算法在我的另一篇博文里已经讲到过了,这里就不再提了。因为灯的数量比较少,所以我就写了一个位运算来常熟优化,但最主要这一道题要思考到,才能够做到。

【代码】

# include <cstdio>
using namespace std;
const int MAXSTATE = 2048;
const int MAXM = 101;
const int MAXN = 11;
const int MAXQ = 5001; /*Faster And Safer : Round Queue*/
const int INF = 10000000; /*Infinite*/

int Q[MAXQ];
int head,tail;
int Qhead,Qtail;
int NowState;
int NewState;
int LightWay[MAXSTATE];
int Way[MAXM][MAXN];

/*N - Light(Point) Number, M - Oper(Way) Number*/
int N,M;
int StateSize;

int main(){
scanf("%d%d",&N,&M);
for (int i=0;i!=M;++i){
for (int j=0;j!=N;++j){
scanf("%d",&Way[i][j]);
}
}

/*Init: All Point Expect Begining's Way is INF*/
StateSize = (1 << N);
for (int i=0;i!=StateSize;++i){
LightWay[i] = INF;
}
LightWay[StateSize - 1] = 0;

/*Init Queue*/
Qhead = Qtail = head = tail = 0;
Q[Qtail] = StateSize - 1;

/*!!!Begin SPFA!!!*/

while (head >= tail){
NowState = Q[Qtail];
for (int i=0;i!=M;++i){
/*To Every Opers*/
NewState = NowState;
for (int j=0;j!=N;++j){
if (Way[i][j] == -1){
NewState = (NewState | (1<<j)); //Set
}
else if (Way[i][j] == 1){
NewState = (NewState & (~(1<<j))); //Clean
}
}

if (LightWay[NewState] > LightWay[NowState] + 1){
LightWay[NewState] = LightWay[NowState] + 1;
++Qhead;
++head;
if (Qhead == MAXQ) Qhead = 0;
Q[Qhead] = NewState;
}
}
++Qtail;
++tail;
if (Qtail == MAXQ) Qtail = 0;
}

if (LightWay[0] == INF){
printf("-1\n");
}
else{
printf("%d\n",LightWay[0]);
}

return 0;
}


洛谷2633 王后万岁

本题地址: http://www.luogu.org/problem/show?pid=2633

题目描述

byteland的王后深受百姓爱戴。为了表达他们的爱,国民们打算占领一个新的国家,并以王后的名字命名。这个国家有n座城市。城市之间有双向道路连接,且每两个城市之间有且仅有一条道路。每座城市对其拥有者来说都有一定的收益。尽管国民们非常爱戴他们的王后,他们并不一定会征服所有的城市献给她。他们只想占领一部分城市(至少有一座),这些城市必须满足两个条件:所有被占领的城市相互间必须是连通的,且城市收益之和最大。你的任务就是算出最大收益是多少。

输入输出格式

输入格式:

第一行是城市的数量n(1<=n<=16000)。第二行包含n个整数,依次表示每座城市的收益,每个数是-1000到1000之间的整数。下面的n-1行描述了道路:每行包含2个整数a和b,用一个空格隔开,表示这两个城市之间有一条道路。

输出格式:

仅有一个数,表示最大收益。

输入输出样例

输入样例#1:
5
-1 1 3 1 -1
4 1
1 3
1 2
4 5
输出样例#1:
4
【题目分析】
其实这一道题我所想到的算法是正确的,但是不会写邻接表,因此用了邻接矩阵爆空间。首先,显而易见这是一个树形DP,因为这个图有n个点,n-1条边,并且全部连通,每两座城市间还只有一条路,所以显然是一棵树,很容易划分状态。
我的想法是,设f(root,1)为以root为根节点的子树,又选择root的最大收益,而f(root,0)表示以root为根节点的子树,不选择root的最大收益,然后最终答案就是max{f(1,1),f(1,0)},或者从0开始计数节点的话就是max{f(0,0),f(0,1)}。那么f(root,1)和f(root,0)怎么求呢?
首先,考虑f(root,1)。如果对于所有root的子节点i,f(i,1)>0的话,选择这第i个节点就能够增加收益,而且还要选择这第i个节点,否则连不起来。最后还要加上root,因为我们的设计中就已经指出要选root了。
其次,考虑f(root,0)。因为root不选,所以无法连接root的子节点,所以只能够将其的值赋为所有root的子节点i中max{f(i,0),f(i,1)}的最大值。因为root不选,所以root的子节点选还是不选就失去了意义,可以直接求最大值。

【算法分析】
这一题因为是树形DP,所以我就只好用了类似记忆化搜索的方法来写。总的来说,这是我第一次写树形DP,但却因为不会写邻接表爆了空间……但好歹挣回了9分充个门面。其实如果可以的话,以后我会将其改为邻接表或前向星一类的。

【代码】

# include <cstring>
# include <cstdio>
using namespace std;
const int INF = 10000000;
const int MAXN = 16001;
const int MAXBOOL = 2;
int f[MAXN][MAXBOOL];
int w[MAXN];
int N;
int a,b;
bool isLink[MAXN][MAXN];
bool find[MAXN];
bool use[MAXN];
bool isLeaf;

inline int max(int a,int b){
return a > b ? a : b;
}

/*记忆化搜索*/
void dfs(int pos){
find[pos] = true;
isLeaf = true;
f[pos][0] = -INF;
f[pos][1] = 0;
for (int i=0;i!=N;++i){
if (isLink[pos][i]){
if (use[i]) continue;
if (!find[i]) dfs(i);
isLeaf = false;
if (f[i][1] > 0){
f[pos][1] += f[i][1];
}
if (max(f[i][0],f[i][1]) > f[pos][0]){
f[pos][0] = max(f[i][0],f[i][1]);
}
use[i] = true;
}
}
if (isLeaf){
f[pos][0] = 0;
f[pos][1] = w[pos];
return;
}
else{
f[pos][1] += w[pos];
}
return;
}

int main() {
memset(isLink,false,sizeof(isLink));
memset(find,false,sizeof(find));
memset(use,false,sizeof(use));
scanf("%d",&N);
for (int i=0;i!=N;++i){
scanf("%d",&w[i]);
}

for (int i=1;i!=N;++i){
scanf("%d%d",&a,&b);
isLink[a-1][b-1] = isLink[b-1][a-1] = true;
}

use[0] = true;
dfs(0);

printf("%d\n",max(f[0][0],f[0][1]));
return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值