NOIP模拟题 2016.10.31 [DP] [搜索] [并查集]

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


这道题是连续的!!!!那么记录pre和suf即可,递推走一遍。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 100005;
int a[maxn];
int n;
int pre[maxn],suf[maxn];
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    a[0]=INF; a[n+1]=-INF;
    for(int i=1;i<=n;i++) pre[i] = a[i-1]<a[i]? pre[i-1]+1 : 1;
    for(int i=n;i>=1;i--) suf[i] = a[i]<a[i+1]? suf[i+1]+1 : 1;
    int ans = 1;
    for(int i=1;i<=n;i++)
    {
        smax(ans,pre[i-1]+1);
        smax(ans,suf[i+1]+1);
        if(a[i+1]-a[i-1]>=2) smax(ans,pre[i-1]+suf[i+1]+1);
    }
    printf("%d",ans);
    return 0;
}

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种可能。


容易发现如果一个区间为两个区间的并,那么就可以无视掉这个区间了,然后dfs走一遍看是否重叠即可。
一开始这么想的。。。。但是忽略了一种情况。。。
假设区间12345,有这样四个区间123,234,1234,23 , 那么还是需要无视掉一个区间,因为用任意三个都可以表示第四个区间,这样发现,实际上是个点的连通性问题。
对于区间[l,r],那么只需要把 l 和 r+1连接起来就表示这段区间已经可以表示了,如果后来的一个区间尝试加入一个区间并且这两个点是联通的(i.e.这个区间已经被恰好覆盖了),那么就舍去。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int mod = 1000000007;
const int maxn = 100005;
int fa[maxn];
int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
inline bool union_find(int x,int y)
{
    int t1=find(x),t2=find(y);
    if(t1==t2) return false;
    fa[t2]=t1;
    return true;
}
int n,m;
int main()
{
    freopen("seg.in","r",stdin);
    freopen("seg.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i;
    int ans = 1;
    for(int i=1;i<=m;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        if(union_find(l,r+1)) (ans<<=1)%=mod;
    }
    printf("%d",ans);
    return 0;
}

来自风平浪静的明天
【题目描述】
冬眠了五年,光终于从梦中醒来。
千咲、要,大家都在。
隐约记得“昨天”的海船祭,爱花意外成为贡女,沉入海底。
海面冰封,却有丝丝暖流在冰面之下涌动。
此时,爱花沉睡在祭海女神的墓地。她的胞衣在一点点脱落,化作一簇簇暖流,夹杂着她的感情,向海面上涌去。
爱花,你在哪里?
五年之后,纺已经成为海洋学研究科的大学生。
在纺的帮助下,光得知了海面下海流的情况。
纺告诉光,暖流一旦产生,就会不断地向四周扩散,直到遇到海中的岩石。
红腹海牛,快告诉光,爱花在哪里。
纺帮你绘制了一张海流情况图,长度为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出的这题= =


一般问题是FloodFill方法来求某时刻的status,然而这个是倒着来求S点。

强行暴力可以拿90%,用bfs尝试从每个点出发。
还有人写A_star的。。启发函数是当前点到所有H点的平均距离。

如果用从边界来bfs,类似于拓扑序的方法,倒着把H变成B,同时更新四周的边界情况,如果非H的个数==3,那么就进队。
但是是错误的方法!!
因为假设当前暖流状况是一个半包围的,那么这样从最外围开始向内填充,最后会停止于内边界,但是实际上应该是中间的某个点。

100%:DP
用dp[step][x][y]表示当前在(x,y),还要走step步是否能达到目标局面,也就是说是个bool。。
那么对于当前这个点,如果是’B’并且step!=0那么当前状态显然是false,如果是石头或者沙滩,那么是true,如果当前step==0,那么返回(g[x][y]==’H’)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 305;
int g[maxn][maxn];
int n,m;
const int dx[] = {-1,0,0,1};
const int dy[] = {0,-1,1,0};
int dp[maxn<<1][maxn][maxn];
inline void init()
{
    scanf("%d%d",&n,&m);
    memset(g,-1,sizeof(g));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            char ch = (char)getchar();
            while(!isalpha(ch)) ch = (char)getchar();
            if(ch=='Y' || ch=='X') continue;
            g[i][j] = (ch=='H');
        }
    memset(dp,-1,sizeof(dp));
}
int dfs(int step,int x,int y)
{
    if(~dp[step][x][y]) return dp[step][x][y];
    int &ans = dp[step][x][y];
    if(!~g[x][y]) return ans = 1;
    if(!step) return ans = (g[x][y]==1);
    for(int k=0;k<4;k++)
        if(!g[x+dx[k]][y+dy[k]]) return ans = 0;
    bool flag = true;
    for(int k=0;k<4;k++) flag = flag&&dfs(step-1,x+dx[k],y+dy[k]);
    return ans = flag;
}
int main()
{
    freopen("calm.in","r",stdin);
    freopen("calm.out","w",stdout);
    init();
    for(int k=n+m;k>=0;k--)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(g[i][j]==1 && dfs(k,i,j))
                {
                    printf("%d %d",i,j);
                    return 0;
                }
    printf("-1");
    return 0;
}

PS.
记录一下2016.10.31万圣节惨痛的经历,爆掉20分创造历史新低。。
第一题没有看到连续导致GG。
痛定思痛

原创文章 152 获赞 15 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ourfutr2330/article/details/52986131
今日推荐