• NOIP2010提高组题解


    (D1T1) 引水入城 ((OK))

    (D1T2) 关押罪犯 ((OK))

    (D1T3) 机器翻译 ((OK))

    (D1T4) 乌龟棋 ((OK))

    这年的一些题目之前都做过好多遍了,所以做得还是比较快的,个人认为最难的是(T1???)

    (T1)要分析出性质:在一个合法方案中(记住这个前提),每个蓄水厂((1,j)),覆盖到的沙漠城市一定会是一段连续的区间([l_j,r_j]).这个吧,想到了就很显然,想不到吧就(GG)了.

    简单证明一下,反证法,如果在一个方案中,有一个蓄水厂((1,j)),它覆盖到的沙漠城市是([l_j,r_j])([l_{j'},r_{j'}])这两段,那么([r_j+1,l_{j'}-1])这一段覆盖不到,肯定是因为它比周围都要高,那么也就是说无论如何这一段都不可能被覆盖得到,即这种方案不合法(这种图是无解的).

    因为数据范围不大,所以我们可以(dfs)预处理出每个蓄水厂((1,j))能覆盖到的区间([l_j,r_j]).

    于是题目就转换为了给你(m)个区间,选出最少的区间覆盖线段([1,m]).就是贪心模板题了.还是因为数据范围很小,所以都不用考虑什么(O(n))贪心,直接(n^2)贪心即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=505;
    int n,m,bj,ans;
    int h[N][N],l[N][N],r[N][N],visit[N][N];
    int dx[4]={0,0,1,-1},
    	dy[4]={1,-1,0,0};
    inline void dfs(int x,int y){
    	visit[x][y]=1;
    	for(int i=0;i<4;++i){
    		int xx=x+dx[i],yy=y+dy[i];
    		if(xx<1||xx>n||yy<1||yy>m)continue;
    		if(h[xx][yy]>=h[x][y])continue;
    		if(!visit[xx][yy])dfs(xx,yy);//!!!!!这里卡了好久,这个点如果之前搜过,虽然不能继续搜下去,但它可以更新其它的点
    		l[x][y]=min(l[x][y],l[xx][yy]);
    		r[x][y]=max(r[x][y],r[xx][yy]);
    	}
    }
    int main(){
    	n=read();m=read();memset(l,0x3f,sizeof(l));
    	for(int j=1;j<=m;++j)l[n][j]=r[n][j]=j;
    	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)h[i][j]=read();
    	for(int j=1;j<=m;++j)if(!visit[1][j])dfs(1,j);
    	for(int j=1;j<=m;++j)if(!visit[n][j])++ans,bj=1;
    	if(bj){printf("0
    %d
    ",ans);return 0;}
    	int L=1,R=0;
    	while(L<=m&&R<m){
    		for(int j=1;j<=m;++j)
    			if(l[1][j]<=L&&r[1][j]>=L)R=max(R,r[1][j]);
    		++ans;L=R+1;
    	}
    	printf("1
    %d
    ",ans);
        return 0;
    }
    
    

    (T2)这道题学并查集的时候做过,学二分图染色的时候又做过.个人认为本题如果选用二分+二分图染色的话,思维难度较低,如果用扩展域并查集的话,代码简洁.博客

    (T3)我爱(STL!!!)这题开个双端队列(deque),然后依据题意模拟即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    deque<int>q;int in[1005];
    int main(){
    	int m=read(),n=read(),ans=0;
    	for(int i=1;i<=n;++i){
    		int x=read();
    		if(!in[x]){
    			if((int)q.size()==m)in[q.front()]=0,q.pop_front();
    			++ans;q.push_back(x);in[x]=1;
    		}
    	}
    	printf("%d
    ",ans);
        return 0;
    }
    
    

    (T4)做过很多次了,因为只有(4)种牌,每种牌的数量又不超过(40),所以可以直接放进状态里,设(f[now][a][b][c][d])表示当前走到了第(now)个格子,还剩(a,b,c,d)这么多牌时的最大得分.

    采用记忆化搜索,转移就只有(4)种,很显然.

    然后交一发,发现编译失败(???)额,数组太大了,可以把第一维直接删掉.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=351;
    int n,m;
    int val[N],sum[5],f[41][41][41][41];
    inline int dfs(int now,int a,int b,int c,int d){
    	if(now>=n)return 0;
    	if(f[a][b][c][d])return f[a][b][c][d];
    	int cnt=0;
    	if(a)cnt=max(cnt,val[now+1]+dfs(now+1,a-1,b,c,d));
    	if(b)cnt=max(cnt,val[now+2]+dfs(now+2,a,b-1,c,d));
    	if(c)cnt=max(cnt,val[now+3]+dfs(now+3,a,b,c-1,d));
    	if(d)cnt=max(cnt,val[now+4]+dfs(now+4,a,b,c,d-1));
    	return f[a][b][c][d]=cnt;
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;++i)val[i]=read();
    	for(int i=1;i<=m;++i)++sum[read()];
    	printf("%d
    ",val[1]+dfs(1,sum[1],sum[2],sum[3],sum[4]));
        return 0;
    }
    
    
  • 相关阅读:
    Entity Framework 6 (7) vs NHibernate 4: DDD perspective(纯净DDD很难很难...)
    asp.net解决高并发的方案
    我所经历的SAP选型[转]
    为什么我不再用 .NET 框架
    Why I Left the .NET Framework
    Docker 传奇之 dotCloud
    ICE概述
    .NET Out Of Memory Exception
    iOS-申请邓白氏编码的超详细流程介绍
    .NET对象与Windows句柄(三):句柄泄露实例分析
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11799697.html
Copyright © 2020-2023  润新知