您的位置:首页 > 其它

暑假作业 三

2015-07-27 07:55 246 查看
打地鼠

小明听说打地鼠是一件很好玩的游戏,于是他也开始打地鼠。地鼠只有一只,而且一共有N个洞,编号为1到N排成一排,两边是墙壁,小明当然不可能百分百打到,因为在他打到地鼠之前他一直不知道地鼠在哪个洞。小明只能在白天打地鼠,而且每次打了都觉得好累,感觉再也不会打了,必须休息到第二天才能再次打地鼠,也就是说他每天只有一次打地鼠的机会。

机智的地鼠在每天晚上都会跑向相邻的两个洞中的一个,如果一边是墙壁就只有往另一边跑,而且它很固执,每天晚上肯定会跑,也就是说不会连续呆在同一个洞。

尽管小明很累,但是他明白要想拿到一等奖,就必须打到地鼠,所以他想知道怎样才能在最坏的情况下最短的天数内保证肯定打到地鼠。

输入

只有一行,该行有一个整数N,表示N个洞并排在一起。地鼠在随机一个洞。

输出

只有一行,该行有一个整数,表示小明肯定能打中至少需要的天数。

样例

mouse.in

1

mouse.out

1

mouse.in

4

mouse.out

4

数据范围

40% n<=10

100%n<=20.

分析:

这是一道找规律的题。

这道题首先是要考虑怎样才能够保证一定能够打中。如果每天选择洞的规律和地鼠移动的规律一样,也就是每天都选前一天相邻的一个洞,那么可以发现,每次你选择的洞口和地鼠所在洞口的距离要么不变,要么增加二或者减少二,按这样从一边墙壁检查到另一边墙壁,如果没有发现地鼠,那就说明地鼠的初始位置和你的初始检查距离为奇数,这个时候再重复检查一次你刚检查过的洞口,那么因为地鼠必须要移动,你们的距离就修改成了偶数,这样再从另一边检查回来,就保证一定能够发现地鼠。这样算下来的结果是2N。

但是可以再分析一下,第一遍扫描的N次的意义在与检查你和地鼠的距离是否为偶数,第二次的意义在于把奇数变为偶数。那么第一次完全可以只从N-1扫描到2(因为地鼠如果在N号洞或者1号洞与你的距离都为奇数,和第一次的任务没有关系),第二次也从2扫描到N-1(因为距离如果是偶数那么相遇时肯定是地鼠与你相向运动,也就是地鼠从相遇点后面过来的,所以不可能会在1号和N号洞相遇)。最后再考虑只有1,和2两个洞这两种特殊情况。

#include <cstdio>

using namespace std;

int n;

int main() {
scanf("%d", &n);

if(n == 1) { printf("1\n"); return 0; }
if(n == 2) { printf("2\n"); return 0; }
printf("%d\n", (n - 2) * 2);

return 0;
}


城墙攻防战、

A国和B国积怨已久,战争不可避免的爆发了,在一次战役中,A国的一支部队包围了B国的一座城池,就在A国以为胜利在望的时候,却殊不知B国正在计划利用坚固的城墙进行反击。

已知B国城池的城墙是由线性排列的N个石块组成,排列由1到N,每个石块都有它的防御值ai,由许多石块连成一段的城墙的防御值等于这段城墙内所有石块防御值之和乘以这段城墙内防御最低的那块石头的防御值。

经过战术商讨,B国决定将敌人引入一段防御最高的城墙将其全歼,但是寻找出这段防御最高的城墙的问题需要他们快速解决。

输出

两行。

第一行一个正整数 N,表示城墙石块的个数。

第二行N个整数,表示每个石块的防御值。

输出

一行

最强防御的城墙的防御力。

样例

wall.in

6

3 1 6 4 5 2

wall.out

60

数据范围

20% 0<=n<=1000;

100% 0<=n<=100000,0<=ai<=1000000.

这道题可以枚举以每个石块为最小值时,他所在的城墙段防御最高是多少,基于贪心原则应该向左和向右尽可能延伸他所在城墙段的区域,这样可以得出最优解。

枚举寻找每个区间的左右界 n^2

改进的话可以使用RMQ(nlogn)或者线段树(n(logn)^2)

还有一种更为暴力的方法,时间效率直逼(O)n,还是用枚举来确立左右边界,如果发现枚举的值小于标准值那么结束枚举,确定边界。如果发现枚举值大于等于标准值,那么直接跳到该枚举点的边界继续枚举。时间复杂度分析,每个标准值只可能遇到一个比它小的枚举值。

每个被枚举的值只可能被一个标准值小于(因为之后他直接被跳过),所以单向枚举的时间复杂度是接近于2n的。

#include <cstdio>
#include <iostream>
using namespace std;

int n;
int a[100000+10];
int lf[100000+10],rf[100000+10];
long long sum[100000+10];//和

void work()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];  //!
lf[i]=i;
rf[i]=i;
}
for(int i=1;i<=n;i++)
{
int x;
for(x=lf[i];a[i]<=a[x-1]&&x-1>0;x=lf[x-1]);
lf[i]=x;//记录lf[i]
for(x=rf[n-i+1];a[n-i+1]<=a[x+1]&&x+1<=n;x=rf[x+1]);
rf[n-i+1]=x;//记录rf[i]
}
long long ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,(long long)a[i]*(sum[rf[i]]-sum[lf[i]-1]));
}
printf("%I64d\n",ans);
}

int main()
{
work();
return 0;
}


屠夫的钩子

在DotA游戏中,屠夫的钩子是英雄们非常惧怕的武器之一,钩子是由N段连续的金属棒构成,一开始钩子的每段金属棒材质均为铜,现在屠夫想增强他武器的威力,决定对他的钩子进行改造,由于智商问题,他一次只能将连续的一段金属棒的的材质变为成金、银、铜的一种,每种材质的一段金属棒的攻击力分别为3,2,1,屠夫想知道按照一系列升级后,他的武器总攻击力为多少,即每个金属棒攻击力之和。

输入

第一行一个数T,表示由T组测试数据。

对于每组数据,第一行一个数N,表示屠夫的钩子的共有N段金属棒,第二行一个数Q,表示屠夫对他的钩子进行了Q次改造,接下来Q行,每行3个数x,y,z,表示屠夫将[x,y]段金属的材质变为z。z=1,表示铜,z=2表示银,z=3表示金。

输出

对于每组测试数据输出改造后的钩子的攻击力,注意使用样例中的格式,各占一行。

样例

hook.in

1

10

2

1 5 2

5 9 3

hook.out

Case 1: The total value of the hook is 24.

分析:

又是一道线段树 。

不多说了。

文件排版

写电子邮件是有趣的,但不幸的是经常写不好看,主要是因为所有的行不一样长,你的上司想要发排版精美的电子邮件,你的任务是为他编写一个电子邮件排版程序。

完成这个任务最简单的办法是在太短的行中的单词之间插入空格,但这并不是最好的方法,考虑如下例子:

This is the example you are

actually considering.

假设我们想将第二行变得和第一行一样长,靠简单地插入空格则我们将得到如下结果:

This is the example you are

actually considering.

但这太难看了,因为在第二行中有一个非常大的空白,如果将第一行的单词“are”移到下一行我们将得到较好的结果:

This is the example you

are actually considering.

当然,这必须对难看程度进行量化。因此我们必须给出单词之间的空格的难看程度,一个包含N个空格符的空白段,其难看程度值为(n-1)2,程序的目的是使难看程度的总和最小化。例如,第一个例子的难看程度是1+7*7=50,而第二个例子的难看程度仅为1+1+1+4+1+4=12。

输出时,每一行的开头和结尾处都必须是一个单词,即每行开头和结尾处不能有空白。唯一例外的是该行仅有一个单词组成的情况,此时如果该单词比该行应有的长度短则我们指定它的难看程度为500。

输入

输入文件第一行是一个整数N,表示排版后文章每行要求达到的宽度,1<=N<=80。

后面是给出的一段文章,该段文章由一个或多个单词组成,单词由ASCII码值为33到126(包含33和126)的字符组成,单词与单词之间用空格隔开(可能超过一个)。单词长度不会超过段落要求达到的宽度。一段文字所有单词的总长度不会超过10000个字符,任何一行都不会超过100个字符,任何一个单词都在同一行内。

输出

找出使其难看程度最小的排版形式并输出句子:“Minimal badness is B.”,B是指按可能的最好排版形式会发生的难看程度值。注意排版后文本行数任意,多余的空格也可删除。

样例

format.in

28

This is the example you are

actually considering.

format.out

Minimal badness is 12.

分析:

这道题是一个动态规划,先预处理一下,字母符号什么的都可以扔了,只留下每个单词的长度,然后用f[i]表示当前已经排好了i个单词并且第i号单词在上一行结尾处文章的最小难看程度,也就是现在是从新一行开始放单词,枚举放多少个单词转移到下一行。每一行的空白都应该平均分配到两个单词之间,余下的空白挨个分一个到单词之间,这样算出的代价是该行最优,最后再考虑一个单词的特殊情况。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int inf=0x3f3f3f;

int n,cnt=0;
int a[10000];
int f[10000];//f[i]表示已放好i个单词,并且i在上一行末尾处文章的最小难看度

void readdata()
{
char s[110];
scanf("%d\n",&n);
while(scanf("%s",s)!=EOF)
{
a[++cnt]=strlen(s);//将每个单词的长度放到a数组
}
}

void work()
{
memset(f,inf,sizeof(f));
for(int i=0;i<cnt;i++)//前i个单词已放好,从下一行开始放单词
{
f[0]=0;
int len=0; //表示该行的单词长度
for(int j=1;;j++)//j为该行单词数
{
int bad=0;//该行的难看程度
if(i+j>cnt)break;//当单词数超过cnt时,退出
len+=a[i+j];//累加该行长度
if(len+j-1>n)break;//当该行最优长度(单词长度+(j-1)个空格长度大于n时,退出
if(j==1&&len<n)bad=500;//当该行只有一个单词且长度小于n时
else if(j==1&&len==n)bad=0;//当该行只有一个单词且长度等于n时
else
{
int k,kk;//平均每个空白的空格数,平均分配后还多出的空格数
k=(n-len)/(j-1);//空格数除以空白数
kk=(n-len)-k*(j-1);//空格数-平均空格数*空白数
bad=(k-1)*(k-1)*(j-kk-1)+kk*k*k;//计算该行的难看度。
}
if(f[i+j]>f[i]+bad)f[i+j]=f[i]+bad;//更新最小难看度
}
}
printf("Minimal badness is %d.\n",f[cnt]);
}

int main()
{
readdata();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  习题