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;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11799697.html