NOIP2011提高组DAY1题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/82142423

链接:NOIP2011DAY2题解:https://blog.csdn.net/Hi_KER/article/details/82180163

T1:铺地毯

考察知识:模拟,枚举

算法难度:X 实现难度:X

分析:直接读入数据然后判断就可以了,真的没有难度

代码:

#include<cstdio>
const int maxn=10005;
int n,a[maxn],b[maxn],g[maxn],k[maxn],x,y;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d%d",a+i,b+i,g+i,k+i);
    scanf("%d%d",&x,&y);
    int ans=-1;
    for(int i=n;i;i--)
	if(a[i]<=x&&b[i]<=y&&x<=a[i]+g[i]&&y<=b[i]+k[i]){
        ans=i;
        break;
    }
    printf("%d\n",ans);
    return 0;
}

T2:选择客栈

考察知识:枚举+分析

算法难度:XXX 实现难度:XX+

分析:

我们记:cnt[i][j]表示(1...j)色调为i的客栈数量 
pre[i]表示i前面第一个最低消费不超过于i的坐标 

那么我们只需要枚举第二个客栈,假设是在i,颜色为col[i],然后统计ans+=cnt[col[i]][pre[i]]

解释:我们选择了第二个客栈,第一个客栈坐标应该小于第二个,而且要有咖啡店,所以坐标应该小于等于pre[i]

注意:pre[i]的求法;如果i的最低消费满足,则pre[i]=i-1

代码:

#include<cstdio>
const int maxn=200005;
int n,k,p,col[maxn],P[maxn];
int cnt[52][maxn],pre[maxn];
long long ans=0;
//cnt[i][j]表示(1...j)色调为i的客栈数量 
//pre[i]表示i前面第一个最低消费不超过于i的坐标 
int main(){
    int pos=0;
    scanf("%d%d%d",&n,&k,&p);
    for(int i=1;i<=n;i++){
        scanf("%d%d",col+i,P+i);
        for(int j=0;j<k;j++) cnt[j][i]=cnt[j][i-1];
        cnt[col[i]][i]++;
        pre[i]=pos;
        if(P[i]<=p) pre[i]=i-1,pos=i;//注意 
    }
    for(int i=n;i>0;i--) ans+=cnt[col[i]][pre[i]];
    printf("%lld\n",ans);
    return 0;
}

T3:Mayan游戏

考察知识:模拟,搜索+剪枝

算法难度:XXXX 实现难度:XXXX

分析:

首先说明一下题意:

a.每次移动只能左移或右移而且只能移动一格

b.操作次数要严格等于n步

解决方法:

a.我们先定义一个结构体,实现方块掉落,方块消除等功能

b.然后开始搜索,直到搜索完毕,这道题剪枝不是非常好想,提供一个剪枝:

如果要向左移但是左移的地方有方块那么剪枝,因为在这种情况下左移,右移等价,而右移字典序小

细节都在代码中(注意宏定义):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define F(var,L,R) for(int var=L;var<=R;var++)
struct mayan_puzzle{
	int g[5][7];
	mayan_puzzle(){memset(g,0,sizeof(g));}
	void fall(){//方块掉落处理 
		F(i,0,4){
			F(j,0,5) if(!g[i][j]&&g[i][j+1]){
				int k=j;
				while(k&&!g[i][k]) k--;
				if(g[i][k]) k++;
				swap(g[i][k],g[i][j+1]);
			}
		}
	}
	bool re_move(){//方块消除处理 
		bool ret=false;
		int r1[5][7],r2[5][7];
		F(i,0,4) F(j,0,6) {//纪录需要消除的位置以及消除的块数 
			r1[i][j]=r2[i][j]=0;
			if(!g[i][j]) continue;
			int k=i+1;
			while(k<5&&g[i][j]==g[k][j]) k++;
			if(k-i>2) r1[i][j]=k-i;
			k=j+1;
			while(k<7&&g[i][j]==g[i][k]) k++;
			if(k-j>2) r2[i][j]=k-j;
		}
		F(i,0,4) F(j,0,6) {
			if(r1[i][j]){ ret=true;
				F(k,0,r1[i][j]-1) g[i+k][j]=0;
			}
			if(r2[i][j]){ ret=true;
				F(k,0,r2[i][j]-1) g[i][j+k]=0;
			}
		}
		if(ret) fall();
		return ret;
	}
	void operate(int x,int y,int k){
		swap(g[x][y],g[x+k][y]);
		fall();
		while(re_move());//注意 
	}
	bool empty(){
		F(i,0,4) if(g[i][0]) return false;
		return true;
	}
	void print(){//仅提供用于查看中间结果 
		for(int i=6;i>=0;i--){
			for(int j=0;j<5;j++) putchar(g[j][i]?g[j][i]+'0':'.');
			putchar('\n');
		}putchar('\n');
	}
}my;
int n,k,x[6],y[6],g[6],findans;
void dfs(int dep,mayan_puzzle T){
	if(dep>n){
		if(T.empty()) findans=1;
		return;
	}
	mayan_puzzle T_;
	F(i,0,4) F(j,0,6) if(T.g[i][j]){
		if(i<4){
			T_=T;
			T_.operate(i,j,1);
			dfs(dep+1,T_);
			if(findans){
				x[dep]=i,y[dep]=j,g[dep]=1;
				return;
			}
		}
		if(i>0&&!T.g[i-1][j]){//包含一个剪枝 
			T_=T;
			T_.operate(i,j,-1);
			dfs(dep+1,T_);
			if(findans){
				x[dep]=i,y[dep]=j,g[dep]=-1;
				return;
			}
		}
	}
}
int main(){
	scanf("%d",&n);
	F(i,0,4) F(j,0,7){//注意j限制应该大于6 
		scanf("%d",&k);
		if(!k) break;
		my.g[i][j]=k;
	}
	dfs(1,my);
	if(findans){
		F(i,1,n) printf("%d %d %d\n",x[i],y[i],g[i]);
	} else printf("-1\n");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/82142423