p1228

这是一道图论呦.

这怎么和图论相关呢?差分约束不是也看起来和图论无关么2333.

那么本题就类似于差分约束.考虑到矩形的每一条边都有一部分露出来,那么可以m*n的用至少四个点更新每个字母的四个顶角位置.然后对于每个有顶角的字母跑它的四个边,如果有别的字母就说明这个字母再本字母之上,连一条有向边.(在这里连的话很大几率使两点间有多个边.考虑到点最多有26个,可以写一个邻接矩阵)

这样就有了一个有向无环图,我们要做的就是按照字典序输出所有可行顺序.这个怎么弄呢?如果只输出一个的话我会维护堆后拓扑排序全部输出的话...依旧欺负它点少,我们直接上一个带dfs的回溯拓扑就好.

我写的还是个邻接表...并且记录入度和是否有点flag.对于从'a'到'z'如果flag==1且入度为0就记录答案,flag改为1,然后dfs冲进去,对于从a到z再判断一下,记录答案,所连边sum--,然后再冲进去.如果dfs层数为n时输出return,并打扫现场.

为什么这样子能做到字典序从小到大呢?因为每次把字母放进ans时是从A到Z的.而邻接表的遍历对然是逆字典序的,但是不影响答案啊.

46 .
65 A
90 Z

虽然字符和数字间可以这样转化,但是实际上并不需要这么严格,字符实际上就是数字.

using namespace std;
int i,f,t;
char ch;
char ans[100];
int n,m;
int l[100],r[100],u[100],d[100],sum[100],flag[100],a[100][100],o[100][100];
struct node{
    int x,y,next;
}e[10000];
int tot,head[100];
void add(int x,int y){
    tot++;
    e[tot].x=x;
    e[tot].y=y;
    e[tot].next=head[x];
    head[x]=tot;
}
void dfs(int now)
{
    if(now==n){
        for(int j=1;j<=n;j++)
            cout<<ans[j];
        cout<<endl;
        return;
    }
    now++;
    for(int k=65;k<=90;k++)
        if(!sum[k]&&flag[k]){
            for(int j=head[k];j!=0;j=e[j].next)
                sum[e[j].y]--;
            ans[now]=k;
            flag[k]=0;//改一下
            dfs(now);
            flag[k]=1;//打扫现场
            for(int j=head[k];j!=0;j=e[j].next)
                sum[e[j].y]++;
        }
}    
int main()
{
    cin>>n>>m;
    memset(l,127,sizeof(l));
    memset(u,127,sizeof(u));
    for(i=1;i<=n;i++)
        for(f=1;f<=m;f++){
            cin>>ch;
            a[i][f]=t=int(ch);
            l[t]=min(f,l[t]);
            r[t]=max(f,r[t]);
            u[t]=min(i,u[t]);
            d[t]=max(i,d[t]);
        }
    for(t=65;t<=90;t++){
        if(r[t]!=0){
            
            f=l[t];
            for(i=u[t];i<=d[t];i++)
                if(a[i][f]!=t)
                    o[t][a[i][f]]=1;
            f=r[t];
            for(i=u[t];i<=d[t];i++)
                if(a[i][f]!=t)
                    o[t][a[i][f]]=1;
            i--;
            for(f=l[t];f<=r[t];f++)
                if(a[i][f]!=t)
                    o[t][a[i][f]]=1;
            i=u[t];
            for(f=l[t];f<=r[t];f++)
                if(a[i][f]!=t)
                    o[t][a[i][f]]=1;
            flag[t]=1;
        }
    }
    n=0;
    for(i=65;i<=90;i++){
        for(f=65;f<=90;f++)
            if(o[i][f])
                sum[f]++,add(i,f);//连边,统计入度
        if(flag[i])//统计总点数n
            n++;
    }
    for(i=65;i<=90;i++){
        if(!sum[i]&&flag[i]){
            for(int j=head[i];j!=0;j=e[j].next)
                sum[e[j].y]--;
            ans[1]=i;
            flag[i]=0;
            dfs(1);//来啊快活啊
            flag[i]=1;//打扫现场
            for(int j=head[i];j!=0;j=e[j].next)
                sum[e[j].y]++;
        }
    }
    return 0;
}

我可能永远也习惯不了这样子的缩进,但还是要努力学习.

让我去吐一会...

转载于:https://www.cnblogs.com/qywyt/p/9690046.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值