2019暑假集训 平板涂色

CE 数码公司开发了一种名为自动涂色机(APM)的产品。它能用预定的颜色给一块由不同尺寸且互不覆盖的矩形构成的平板涂色。

为了涂色,APM 需要使用一组刷子。每个刷子涂一种不同的颜色。APM拿起一把蘸有颜色 C的刷子 。并给所有颜色为C且符合下面限制的矩形涂色。为了避免颜料渗漏导致颜色混合,一个矩形只能在所有紧靠它上方的矩形涂色后,才能涂色。例如图中矩形 F 必须在 C 和 D 涂色后才能涂色。注意,每一个矩形必须立刻涂满,不能只涂一部分。
写一个程序求一个使 APM 拿起刷子次数最少的涂色方案。注意,如果一把刷子被拿起超过一次,则每一次都必须记入总数中。

输入
第一行为矩形的个数 N 。
下面有 N 行描述了 N 个矩形。每个矩形有 5 个整数描述,左上角的 y 坐标和 x 坐标,右下角的 y 坐标和 x 坐标,以及预定颜色。
颜色号为 1 到 20 的整数。平板的左上角坐标总是 (0,0),坐标的范围是0…99。N小于16。
输出
输出一个整数,表示拿起刷子的最少次数。
样例输入
7
0 0 2 2 1
0 2 1 6 2
2 0 4 2 1
1 2 4 4 2
1 4 3 6 1
4 0 6 4 1
3 4 6 6 2
样例输出
3

也就是说 这些矩形之间的涂色是有顺序的 所以我们考虑dfs?
但本题具有后效性 每次涂色需要检查上面的块是不是都涂过了
怎么检查这种涂过了的关系呢?
首先我们知道第i个块上面所有的块 当上面所有的块都满足一定条件才能进行下一步
什么样的结构可以表示这样的关系?有向图!!!
于是我们引入一个新的概念:

说人话就是 保证对于DAG上每条边的两个端点 在得到的序列中 出发点一定在结束点之前
这样就有一个好处 我们将涂矩形i所需所有的矩形j都将(j,i)连边 只有当一个点前面的点都出现在序列中后 这个点才能出现在序列中 这个序列称为拓扑序列
为了理想化这个算法 我们将每个点被多少条边所指称为这个点的入度
比如像下面这样:

图源洛谷dalao @_J_C_ 感谢
圆圈上面的数就是这个点的入度
按照刚才的方法 我们如果每经过一个点将这个点引出的所有边删去(即这个点后继的所有点入度-1) 每次找到入度为0的点(即不依赖任何点的点)即可
同时由于我们需要尽可能使相同颜色的连在一起 每次当有不同选择时进行搜索即可
上代码
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
struct node
{
    int lx,ly,rx,ry,c,in;
}a[20];//in是该点入度
struct edge
{
    int u,v,nxt;
}e[50050];
int n,num,head[20],ans=0x3f3f3f3f;
void add(int u,int v)
{
    e[++num].u=u,e[num].v=v;
    e[num].nxt=head[u],head[u]=num;
}//邻接表建图
void dfs(int now,int cnt)
{
    a[now].in=-1;//表示这个点已经经过了
    int flag=0;
    for(int i=1;i<=n;i++)
        if(a[i].in!=-1)
        {
            flag=1;
            break;
        }//判断是否所有矩形已经涂完
    if(!flag)
    {
        for(int i=head[now];i!=-1;i=e[i].nxt)
            a[e[i].v].in++;    
        a[now].in=0;
        ans=min(ans,cnt);
        return;
    }//更新最小值,返回上级调用
    for(int i=head[now];i!=-1;i=e[i].nxt)a[e[i].v].in--;//引出的所有点入度减一
    flag=0;
    for(int i=1;i<=n;i++)
        if(a[i].in==0&&a[i].c==a[now].c)
            flag=1,dfs(i,cnt);//如果下个入度为0的点颜色与这个点相同则按编号搜
    if(!flag)
        for(int i=1;i<=n;i++)
            if(a[i].in==0)dfs(i,cnt+1);//反之直接搜
    for(int i=head[now];i!=-1;i=e[i].nxt)
        a[e[i].v].in++;    
    a[now].in=0;//回溯
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d%d%d",&a[i].ly,&a[i].lx,&a[i].ry,&a[i].rx,&a[i].c);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)continue;
            int temp=abs(a[i].rx-a[i].lx)+abs(a[j].rx-a[j].lx);
            if(a[i].ry==a[j].ly&&abs(a[i].rx-a[j].lx)<temp&&abs(a[i].lx-a[j].rx)<temp)//判断是否正好在上面
            {
                add(i,j);
                a[j].in++;
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(a[i].in==0)dfs(i,1);//找到每个入度为0的点
    printf("%d",ans);
    return 0;
}


 
/*====年轻人,瞎搞是出不了省一的,这就是现实====*/
原文地址:https://www.cnblogs.com/qxds/p/11170538.html