问题描述
给出一个表格, N 行 M 列,每个格子有一个整数,有些格子是空的。现在需要你
来做出一些调整,使得每行都是非降序的。这个调整只能是整列的移动。
输入
第一行两个正整数 N 和 M。
接下来 N 行,每行 M 个整数, -1 表示这个格子是空的,其他的整数都在 [0, 10^9]范围,表
示格子的数字。
输出
若无解,输出 -1;
否则输出任意一个解,即一行 M 个正整数 p1, p2, · · · , pm,表示可以把初始表格的 pi 列,
放在新表格的第 i 列,以得到一个合法的表格。
样例输入 1
3 3
-1 -1 -1
2 1 2
2 -1 1
样例输出 1
2 3 1
样例输入 2
2 2
1 2
2 1
样例输出 2
1
数据规模与约定
对于 20% 的数据,满足 1 ≤ N ≤ 8, 1 ≤ M ≤ 8。
对于 60% 的数据,满足 1 ≤ N × M ≤ 2 × 10^3。
对于 100% 的数据,满足 1 ≤ N × M ≤ 10^5。
题解
对每一列小到大排序,忽略-1 的。排前面的列,向后面的建边。因为有多个相同数字的,
所以建一些虚点。
如
2 2 3 3
在 2,3 之间建一个虚点, 2 2 向虚点建边,虚点向 3 3 建边
最后跑拓扑排序
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#define PB(v) push_back(v)
using namespace std;
const int N=5e5+10;
int n,m;//n行m列
struct node{
int num,idy;//数值和初始列序号
bool operator <(const node&rhs)const{
return num<rhs.num;}
}table[N];
vector<int>vx[N];//建图用
queue<int>q;//存所有入度为0的点
int deg[N];//单行中每列的入度
int ans[N];//记录可行解
int main()
{
freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);//n行m列
int xd=m;//虚点下标
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)//读入1行中每列的数值
{
scanf("%d",&table[j].num);
table[j].idy=j;
}
sort(table,table+m);//对一行的值进行排序
for(int k=-1,l=0,r=0;l<m;l=r)
{
//k为建立的虚点,l为相同区间左端点,r为右端点
while(r<m&&table[r].num==table[l].num)r++;
if(table[l].num==-1)continue;
if(k!=-1)for(int o=l;o<r;o++)vx[k].PB(table[o].idy),deg[table[o].idy]++;
//虚点已经建立,则虚点向后连边
k=xd++;//新建虚点
for(int o=l;o<r;o++)vx[table[o].idy].PB(k),deg[k]++;//将之前的点向虚点连边
}
}
int top=0;
for(int i=0;i<xd;i++)
if(!deg[i])q.push(i);
while(!q.empty())//拓扑排序
{
int u=q.front();
q.pop();
if(u<m)ans[top++]=u;
for(int i=0;i<vx[u].size();i++)
{
int v=vx[u][i];
if(--deg[v]==0)q.push(v);
}
}
if(top<m)puts("-1");
else for(int i=0;i<m;i++)printf("%d%c",ans[i]+1,i==m-1?'\n':' ');
return 0;
}
比赛时完全没思路……对拓扑排序很不熟