2017.9.17 胡策题 【题解 + 总结】【NOIP模拟】
[1]Mushroom的序列
【问题描述】
Mushroom手中有n个数排成一排,现在Mushroom想取一个连续的子序列,使得这个子序列满足:最多只改变一个数,使得这个连续的子序列是严格上升子序列,Mushroom想知道这个序列的最长长度是多少。
【输入格式】
第一行一个整数n,表示有n个数。
第二行为n个数。
【输出格式】
一个数,为最长长度。
【输入样例】
6
7 2 3 1 5 6
【输出样例】
5
【样例解释】
选择第2个数到第6个数,把1改变成4即可。
【数据范围】
对于30%的数据,n<=10
对于60%的数据,n<=1000
对于100%的数据,n<=100000
本题虽然是一道水题 , 但是细节方面还是值得深究 ;
求出以一个点为开头和结尾的最长上升序列 , 然后枚举中间那个要改动的点有一个细节需注意就是合并的情况有三种 , 一是不合并 , 二是只合并一个数与与一个序列 , 三是以一个数为中间修改数来将两个序列合并起来 , 代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100001
using namespace std;
int n , num[M] , lon_nex[M] , lon_pre[M] , ans = 0;
int main(){
scanf("%d",&n);
lon_pre[n + 1] = (1 << 31) , lon_nex[0] = (1 << 31) - 1;
for(int i = 1 ; i <= n ; i++)scanf("%d",&num[i]);
for(int i = n ; i ; ans = max(ans , (i > 1 ? lon_pre[i] + 1 : lon_pre[i])) , i--)lon_pre[i] = (num[i + 1] > num[i] ? lon_pre[i + 1] + 1 : 1);
for(int i = 1 ; i <= n ; ans = max(ans , (i < n ? lon_nex[i] + 1 : lon_nex[i])) , i++)lon_nex[i] = (num[i] > num[i - 1] ? lon_nex[i - 1] + 1 : 1);
for(int i = 1 ; i <= n ; i++)
if(num[i - 1] < num[i + 1] - 1)ans = max(lon_nex[i - 1] + lon_pre[i + 1] + 1 , ans);
printf("%d",ans);
return 0;
}
[2]Mushroom的区间
【题目描述】
Mushroom有一行数,初始时全部是0。现在Mushroom有m个区间[L,R],他希望用以下操作得到新的序列。
从m个给定区间中选择一个区间[s,t],把区间中的数对应元素全部翻转。(0变1,1变0)
请告诉Mushroom他能得到多少序列。(模10^9+7)
【输入格式】
第一行包含两个整数n,m。表示n个数和m个区间。
接下来m行是所表示的区间。
【输出格式】
一个整数,表示能得到的区间数。
【样例输入】
3 3
1 1
2 2
3 3
【样例输出】
8
【数据范围】
对于30%的数据,n,m<=20
对于60%的数据,n,m<=100
对于100%的数据,n,m<=100000
【样例解释】
每个位置都可以单个修改,所以有8种可能。
该题有一个思想就是判断是否会对答案做出贡献 , 意思是若选择一个新的区间能使答案总数变多则称为对答案有贡献 , 本题只是要求序列不同而不是选择的区间不同 , 所以若一个区间能能被其他区间完全覆盖 , 则这个区间不会对答案做出贡献(因为他做出的已经被其他的区间组合覆盖 , 他不能创造出新的序列),本题目变成了找多少个不会出现一个区间能能被其他区间完全覆盖的情况的区间数 , 这里便可以用并查集来解决: 一个区间能否被其他区间组合而成便相当于一个区间的端点能否被其他区间的端点通过“异或”来达成
但是此题需要注意一个细节就是有(n,n)这种单独点对的存在 , 所以为了是这种情况合法(可以做出贡献的可能),所以需要是将所有区间扩展一位(代码中的x-1 , y)
代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 100001
using namespace std;
int n , m , p[M] , ans = 1;
const int mod = 1000000007;
int find(int k){return p[k] == k ? k : p[k] = find(p[k]);}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= n ; i++)p[i] = i;
for(int i = 1 ; i <= m ; i++){
int x , y;
scanf("%d%d",&x,&y);
int fx = find(x - 1) , fy = find(y);
if(fx != fy){
p[fx] = fy;
ans = ans * 2 % mod;
}
}
printf("%d\n",ans);
}
[3]来自风平浪静的明天
【题目描述】
冬眠了五年,光终于从梦中醒来。
千咲、要,大家都在。
隐约记得“昨天”的海船祭,爱花意外成为贡女,沉入海底。
海面冰封,却有丝丝暖流在冰面之下涌动。
此时,爱花沉睡在祭海女神的墓地。她的胞衣在一点点脱落,化作一簇簇暖流,夹杂着她的感情,向海面上涌去。
爱花,你在哪里?
五年之后,纺已经成为海洋学研究科的大学生。
在纺的帮助下,光得知了海面下海流的情况。
纺告诉光,暖流一旦产生,就会不断地向四周扩散,直到遇到海中的岩石。
红腹海牛,快告诉光,爱花在哪里。
纺帮你绘制了一张海流情况图,长度为N,宽度为M。
海很大,一边有沙滩,一边一望无际,但长度和宽度都不会超过300。沙滩是金黄色的,所以用Y表示。海是蓝色的,所以用B表示。暖流很暖和,所以用H表示
海中有大大小小的石头。石头很危险,所以用X表示
光相信自己一定能找到爱花(爱花的位置只有一种可能)
【输入格式】
第一行包括两个整数N,M。
接下来N行,每行M个字符。
【输出格式】
仅一行,表示爱花的位置(如果你无能为力,请输出 -1 ,只要你尽力,光不会责怪你)
【样例输入】
5 5
YYYHB
YYHHH
YHHXB
BBHBB
BBBBB
【样例输出】
2 3
【数据范围】
对于30%的数据,n,m<=10
对于70%的数据,n,m<=100
对于100%的数据,n,m<=300
【样例解释】
在(2,3)出现第一个H后,经过3s后,出现样例输入的地图。
P.S. Mushroom拜托他GF出的这题= =
这道题。。。由于是300的范围嘛。。。所以咱不可以直接用O(n^2)的枚举就可以了么。。。从边界开始,由于暖流是每次都会向四周扩展所以重边界往回溯 , 直到找到最后一个点便是爱花在的位置,若是bfs最后剩下的是偶数个点,则就找不到(不能确定是哪里)
#include <cstdio>
#include <iostream>
#define INF 300
#include <algorithm>
using namespace std;
char Map[INF+1][INF+1];
bool Find[2*INF+1][INF+1][INF+1];
bool Flow[2*INF+1][INF+1][INF+1];
int N,M;
bool LoveFlower(int Deep,int X,int Y)
{
if (0==X) return true;
if (0==Y) return true;
if (N+1==X) return true;
if (M+1=]p=Y) return true;
if (Map[X][Y]!='H') return true;
if (Find[Deep][X][Y]) return Flow[Deep][X][Y];
if (0==Deep)
{
Find[Deep][X][Y]=true;
Flow[Deep][X][Y]=(Map[X][Y]=='H');
return Flow[Deep][X][Y];
}
if ('B'==Map[X-1][Y]||'B'==Map[X+1][Y]||'B'==Map[X][Y-1]||'B'==Map[X][Y+1])
{
Find[Deep][X][Y]=true;
Flow[Deep][X][Y]=false;
return Flow[Deep][X][Y];
}
Find[Deep][X][Y]=true;
Flow[Deep][X][Y]=LoveFlower(Deep-1,X-1,Y)&&LoveFlower(Deep-1,X+1,Y)&&LoveFlower(Deep-1,X,Y-1)&&LoveFlower(Deep-1,X,Y+1);
return Flow[Deep][X][Y];
}
char T[INF+1];
int main()
{
freopen("calm.in","r",stdin);
freopen("calm.out","w",stdout);
scanf("%d%d",&N,&M);
for (int i=1;i<=N;i++)
{
scanf("%s",T);
for (int j=1;j<=M;j++)
Map[i][j]=T[j-1];
}
for (int i=1;i<=N+M;i++)
for (int j=1;j<=N;j++)
for (int k=1;k<=M;k++)
if (Map[j][k]=='H')
LoveFlower(i,j,k);
for (int i=N+M;i>=1;i--)
for (int j=1;j<=N;j++)
for (int k=1;k<=M;k++)
if (Flow[i][j][k])
{
cout<<j<<" "<<k<<'\n';
return 0;
}
return 0;
}
总结:
本次题目在算法灵活度和细节上还是有点要求 , 第一题第二题需要把问题想深想细 , 把所有可能的情况都讨论出来 , 然后再去依次写对应的特判;而在知识点的灵活性方面,也需要有一定的加强 , 这个问题是什么 , 满足条件情况的最基本特性是什么 , 然后是什么算法可以解决相应的特性 ;都需要有足够的熟练度,觉得后期也应该多多刷一些题目才是。。。
下一篇: 2017.8.26 noip模拟赛 总结