一个由1*w*nw的积木构成的长方体,每次抽掉一块,问啥时候会倒塌。
直接模拟每一次抽取,从改层往下计算上面木块的重心,记录每一层最左位置和最右位置,判断重心是否偏离最左或者最右或者在边界上即可。
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN=5000+10;
int n,w;
int h,m;
int l,k;
int vis[MAXN][MAXN];
double x[MAXN],y[MAXN];//往上所有层的重心
int num[MAXN];
double L[MAXN],R[MAXN];
double xx[MAXN],yy[MAXN];//每一层的重心
const double EPS=1e-12;
int main(){
freopen("jenga.in","r",stdin);
freopen("jenga.out","w",stdout);
scanf("%d%d%d%d",&n,&w,&h,&m);
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
memset(x,0,sizeof x);
memset(y,0,sizeof y);
memset(xx,0,sizeof xx);
memset(yy,0,sizeof yy);
num[h+1]=0;
for(int i=h;i>=1;i--){
L[i]=0;
R[i]=n*w;
num[i]=num[i+1]+n;
for(int j=1;j<=n;j++){
x[i]=(n*w*1.0/2.0);
y[i]=(n*w*1.0/2.0);
xx[i]=x[i];
yy[i]=y[i];
}
}
int ok=-1;
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&k);
if(ok!=-1)
continue;
vis[l][k]=1;
for(int j=l;j>=1;j--){//对所有l层往下的num值-1
num[j]--;
if(num[j]==num[j+1]||num[h]==0){
ok=i;
break;
}
}
if(ok!=-1){
continue;
}
//更新左右端点值
if(num[l]==num[l+1]){
ok=i;
break;
}else{
for(int j=1;j<=n;j++){
if(!vis[l][j]){
L[l]=(j-1)*w*1.0;
break;
}
}
for(int j=n;j>=1;j--){
if(!vis[l][j]){
R[l]=j*w*1.0;
break;
}
}
}
//对l层计算新的重心
double temp=0;
double tmp=0;
for(int j=1;j<=n;j++){
if(!vis[l][j]){
temp+=((j-1)*w+w/2);
tmp+=1.0;
}
}
temp/=tmp;
if(l&1){
xx[l]=temp;
}else{
yy[l]=temp;
}
for(int j=l;j>=1;j--){
x[j]=(x[j+1]*num[j+1]+xx[j]*(num[j]-num[j+1]))/num[j];
y[j]=(y[j+1]*num[j+1]+yy[j]*(num[j]-num[j+1]))/num[j];
if(j!=h&&(j&1)&&((x[j+1]<=L[j]||x[j+1]>=R[j])||(fabs(x[j+1]-L[j])<EPS)||(fabs(x[j+1]-R[j])<EPS))){
ok=i;
break;
}else if(j!=h&&!(j&1)&&((y[j+1]<=L[j]||y[j+1]>=R[j])||(fabs(x[j+1]-L[j])<EPS)||(fabs(x[j+1]-R[j])<EPS))){
ok=i;
break;
}
}
}
if(ok==-1){
puts("no");
}else{
puts("yes");
printf("%d\n",ok);
}
}