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了一上午。