欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NOIP2011提高组DAY1题解

程序员文章站 2024-03-20 11:54:52
...

链接: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;
}

 

相关标签: NOIP 题解