Frame Up_usaco 4.4_拓扑排序

本文解析了一个USACO竞赛中的图像堆叠问题,通过定义图像堆叠规则和使用拓扑排序的方法来解决如何正确堆叠矩形图像的问题。文章详细介绍了算法思路,包括如何确定图像之间的堆叠顺序及解决拓扑排序不唯一的情况。

Description


看下面的五张 9 x 8 的图像:
这里写图片描述
现在,把这些图像按照 1—5 的编号从下到上重叠,第 1 张在最下面,第 5 张在最顶端。如果一张图像覆盖了另外一张图像,那么底下的图像的一部分就变得不可见了。我们得到下面的图像:
这里写图片描述
对于这样一张图像,计算构成这张图像的矩形图像从底部到顶端堆叠的顺序。
下面是这道题目的规则:
• 矩形的边的宽度为 1 ,每条边的长度都不小于 3 。
• 矩形的每条边中,至少有一部分是可见的。注意,一个角同时属于两条边。
• 矩形用大写字母表示,并且每个矩形的表示符号都不相同。

Input


第一行 两个用空格分开的整数:图像高 H (3 <= H <=30) 和图像宽 W (3 <= W <= 30) 。
第二行到第 H+1 行 H 行,每行 W 个字母。

Output


按照自底向上的顺序输出字母。如果有不止一种情况,按照字典顺序输出每一种情况(至少会有一种合法的顺序)。

Analysis


我tm又双叒叕被usaco恶心到了
首先确定每个字母所在矩形,如果在A字母的矩形上找到了B字母,那么连边B->A,然后就是拓扑排序
需要注意的是拓扑序不唯一,所以我们需要枚 所有可能的拓扑序然后记录解,最后排序
打完才觉得不算难的一题

Code


/*
ID:wjp13241
PROG:frameup
LANG:C++
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define dfo(i,a,b) for (int i=a;i>=b;i--)
#define fil(x,t) memset(x,t,sizeof(x))
#define STP system("pause")
#define min(x,y) x<y?x:y
#define max(x,y) x>y?x:y
#define ll long long
#define INF 0x7f7f7f7f
#define EPS 1e-4
#define L 351
#define N 351
#define E N*N+1
using namespace std;
struct word{int u,d,l,r;}t[27];
struct edge{int y,next;}e[E];
string ans[E];
int p[N][N],vis[N],map[N][N],ls[E],ind[N],bct[27],maxE=0,num=0;
int add(int x,int y){e[++maxE]=(edge){y,ls[x]},ls[x]=maxE;};
int topsort(string lis,int cnt,int tot)
{
    if (cnt==tot)
    {
        ans[++num]=lis;
        return 0;
    }
    fo(x,1,26)
        if (!ind[x]&&t[x].d)
        {
            for (int i=ls[x];i;i=e[i].next)
                ind[e[i].y]--;
            ind[x]=-1;
            topsort((char)(x-1+'A')+lis,cnt+1,tot);
            ind[x]=0;
            for (int i=ls[x];i;i=e[i].next)
                ind[e[i].y]++;
        }
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    fo(i,1,26)
        t[i]=(word){INF,0,INF,0};
    fo(i,1,n)
    {
        char s[L];
        cin>>s;
        fo(j,0,m-1)
        {
            if (s[j]!='.')
                map[i][j+1]=s[j]-'A'+1;
            else
                map[i][j+1]=0;
            t[map[i][j+1]]=(word){min(t[map[i][j+1]].u,i),max(t[map[i][j+1]].d,i),min(t[map[i][j+1]].l,j+1),max(t[map[i][j+1]].r,j+1)};
        }
    }
    int tot=0;
    fo(i,1,26)
    if (t[i].d)
    {
        tot++;
        fo(k,t[i].u,t[i].d)
        {
            int now=map[k][t[i].l];
            if (now&&now!=i&&!p[now][i])
            {
                p[now][i]=1;
                add(now,i);
                ind[i]++;
            }
            now=map[k][t[i].r];
            if (now&&now!=i&&!p[now][i])
            {
                p[now][i]=1;
                add(now,i);
                ind[i]++;
            }
        }
        fo(k,t[i].l,t[i].r)
        {
            int now=map[t[i].u][k];
            if (now&&now!=i&&!p[now][i])
            {
                p[now][i]=1;
                add(now,i);
                ind[i]++;
            }
            now=map[t[i].d][k];
            if (now!=i&&!p[now][i])
            {
                p[now][i]=1;
                add(now,i);
                ind[i]++;
            }
        }
    }
    topsort("",0,tot);
    sort(ans+1,ans+num+1);
    fo(i,1,num)
        cout<<ans[i]<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值