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