2019牛客国庆集训派对day1 G 字典序 —— 想法

This way

题意:

有一个n*m的矩阵,现在让你构造长度为m的排列,使得所有行的数按照这个排序之后字典序从上到下递增,若有多种解,输出字典序最小的一个。

题解:

一般字典序的题目的解法 是贪心,并且这道题贪心是可行的。对于每次选的话,我们不能 n 2 n^2 判断,需要快速判断每一列是否可行。使用一个数组表示当前有多少不允许范围内递减的数:
在这里插入图片描述
假设当前的列为12234,那么2-3在之后是不允许递减的,其它是允许递减的。所以对于每一次选择列之后,对于没有运算过的行,如果当前选择的列中第i行与第i+1行是递增的并且之前没有做过这个操作,我们将所有列都置为允许递减,也就是如果别的列第i个与第i+1个是递减的,那么消去这个影响。由于每一行最多被消去影响一次,总时间复杂度是可以保证的。
有点拗口。。

#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5;
int mp[N][N],desc[N],ans[N];
bool vis[N],ers[N];
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        memset(vis,0,sizeof(vis));
        memset(ers,0,sizeof(ers));
        memset(desc,0,sizeof(desc));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&mp[i][j]),desc[j]+=mp[i][j]<mp[i-1][j];
        int f=0;
        for(int t=1;t<=m;t++)
        {
            int p=1;
            for(;p<=m;p++)
            {
                if(vis[p])continue;
                if(!desc[p])break;
            }
            if(p>m){f=1;break;}
            ans[t]=p,vis[p]=1;
            for(int i=1;i<n;i++)
            {
                if(ers[i]||mp[i][p]>=mp[i+1][p])continue;
                for(int j=1;j<=m;j++)
                {
                    if(vis[j])continue;
                    if(mp[i][j]>mp[i+1][j])desc[j]--;
                    ers[i]=1;
                }
            }
        }
        if(f)
        {
            printf("-1\n");
            continue;
        }
        for(int i=1;i<=m;i++)
            printf("%d%c",ans[i]," \n"[i==m]);
    }
    return 0;
}

发布了530 篇原创文章 · 获赞 31 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/101976357