D:https://codeforces.com/problemset/problem/1428/D
题意:给你n * n 的格子,你可以在任意格子放上障碍。现在会从每一列的最底下掷出一个回力镖,它会沿着格子向前,遇到障碍后会使之向右转向90°(注意这个转向是相对于当前方向的转向)简单的说,如果现在向上,遇到障碍后会变成向右,如果是向右的,直接变成向下。向下的就会向左。因为题目里面说了,最多碰撞3次,向左到向上的情况就不用考虑了。题目给你从每一列进入的回力镖会碰到的障碍的数目(记为 ,要你构建一个符合题意的障碍物分布图输出出来。障碍物可用的数目是0~2n。注意题目的限制,每行每列至多两个障碍物。
思路:由于是向右转向,不难想到从右边开始构造。
对于第i列,如果它的 ,那么就是在这一列的任意位置放上一个障碍物,放哪里呢?不妨放到第i行上去,这样有规律可言。
那么对于想要弹射两次,那么必然在它的右边存在一个障碍物。 并且这个障碍物所在的行还只能拥有它自己一个障碍物。所以,这些障碍物就应该放在之前那些 的列障碍物所在的行上。
至于 想要弹射三次,就代表它需要它的正右方,和右下方有障碍物。注意我们在这题里面放障碍物的方式,恰好是从右下角开始放的,这也就表示我们只要考虑这个障碍物正右方的障碍物的位置即可,值得注意的一点,这个正右方的障碍物,必定在某一个其他障碍物的上方,也就是要求那一列上本身存在着障碍物。不难发现,其实不论 的值为多少(除开0),第i列在初始状态下必然都只有一个障碍。那么也就是对于 它可以摆在第i行后。它右边的障碍可以摆在任意位置。
我们用三个队列来记录目前为止所遇到的1,2,3(用什么来记录都可以)。
遇到1时,把对应的列数放入队列。
当遇到2时,看1队列是否为空。如果为空,表示此时在每行每列至多两个障碍物的限制条件下,不存在可以摆下一个障碍物的行。否则,把障碍物摆在2队列队头障碍物所在的行。队头出队。同时2队列放入相应列。
当遇到3时,优先看3队列空不空,其实对于3而言,只要花费一个前面的1/2,就可以实现自循环,不再对1/2产生消耗。你自己想想看。那么第一个3就是一个问题,也就是填在之前的1/2的上面,这样一来就完成了。同时对3队列进行维护。如果花费了1/2,也要相应的维护。至此,代码也就写出来了。
//team yglance+xhwl+TTD //author: CN.TTDragon #include<bits/stdc++.h> typedef long long ll; const ll mod=1e9+7; const ll maxn=1e5+7; const double pi=acos(-1); using namespace std; vector < int > lie[maxn];//每一列都有那些行被放了数 //三个队列 queue<int > yi; queue<int > er; queue<int > san; //总数 int num=0; int n; int a[maxn]; int hang; int temp; int shu; //这个就是每一列应该填哪一行,也可以不要,直接第j列就放第j行。我在这里搞复杂了。 int f(int n,int k) { if(k==0)//k=0 直接跳过就好 { return 1; } else if(k==1)//等于1 放个障碍就好, { lie[n].push_back(shu--); yi.push(n); num++; return 1; } else if( k==2 ) { if(yi.empty())//如果一被用完了,只能遗憾结束构造了。 { return 0; } hang=lie[yi.front()][0];//弹射次数为1 的列中 障碍物所在的行数。 yi.pop(); lie[n].push_back(hang); er.push(n);//2队列的维护 num++; return 1; } else if(k==3) { if(yi.empty()&&er.empty()&&san.empty())//真·非 { return 0; } if(!san.empty())//务必先做这个,毕竟有自循环在。 { lie[n].push_back(shu); num++; //我想知道那个1 在那一列 temp=san.front(); san.pop(); lie[temp].push_back(shu);// n hang temp lie 加一个障碍物 num++; shu--; san.push(n); return 1; } if(!er.empty())//你也看出来了,1是很珍贵的,大家都要用,所以先考虑2. { lie[n].push_back(shu); num++; //我想知道那个2 在那一列 temp=er.front(); er.pop(); lie[temp].push_back(shu);// n hang temp lie 加一个障碍物 num++; san.push(n); shu--; return 1; } if(!yi.empty())//实在不行再用1. { lie[n].push_back(shu); num++; temp=yi.front(); yi.pop(); lie[temp].push_back(shu);// n hang temp lie 加一个障碍物 num++; san.push(n); shu--; return 1; } } } int main() { ios::sync_with_stdio(false); //freopen("in.in","r",stdin); //freopen("mine.out","w",stdout); cin>>n; shu=n; int ling =0; int fff=1; for(int i=1;i<=n;i++) { cin>>a[i]; if(a[i]==0) { ling++; } } if(ling==n) { cout<<0<<endl; return 0; } for(int i=n;i>=1;i--) { fff=f(i,a[i]); if(fff==0) { cout<<-1<<endl; return 0; } } cout<<num<<endl; for(int j=1;j<=n;j++) { for(int i=0;i<lie[j].size();i++) { cout<<lie[j][i]<<" "<<j<<endl; } } return 0; }
写在最后:
挺简单一题,因为没想明白3队列的使用,一开始把它摆在1和2之后,WA了一上午。