CF 274D Lovely Matrix 拓扑排序中缩点使用 个人心得
题目传送门:http://codeforces.com/contest/274/status/D
题目大意:给出一个n*m的矩阵,问,是否能通过改变列的顺序,使得每一行都非递减,-1表示这个数未知(可以为任意数)。
解题思路:典型的拓扑排序题,但是因为各元素可以相等,而简单的拓扑排序限定了元素之间必须是一一对应,无法做到使相等的元素平行,所以这里就需要加入辅助点(冗余点)来达到允许元素平行这一目的。
最开始我遇到这个问题时,想到的是通过讲值相同的元素组与其他元素一一建立联系(下面代码中被注释掉的部分就是),这样的确可以解决一对多、多对一的问题,但是,他不能解决一对一的问题,而且操作麻烦。所以我看了别人的代码,找到了正确的解题方案。以下是我在对这种方法的理解上的心得:
- 辅助点原理:
- 因为:
- 每一组相同值单位都需要同时被释放,同时可用
- 每一组相同值单位需要被全部释放后,它的后续值单位(指向的下一个)才能被完全释放
- 所以:
- 对于每一轮的限制,都增加一组辅助点,其数量等于次轮中不同值的元素的数量,所以对于n*m次的入度计算,需要的辅助点数量<=n*(m+1)
- 辅助点用来“存储”下一个要释放的元素下标和释放它所需要的先被释放的元素下标
- 这样,每个元素之间的限制关系就不再只是一对一,而可以是一对多、多对一或多对多,所以就能解决在拓扑排序里,平行元素组和其他单个元素或其他平行元素组之间不能建立简单大小关系的问题
代码(自己写的,可能有很多不足):
#include<bits/stdc++.h>
#define M 200010
using namespace std;
vector<int> re[M];
queue<int> que;
int ro[M],n,m,t,ans[M],er[M],cc;
struct S{
int v,x;
bool operator < (const S t)const{
return v<t.v;
}
}a[M];
///原来的方法所用的topo,效率不高,但易于理解
int topo1(){
int n=1,aim=-1;
for(int i=1;i<=m;++i){
for(int j=1;j<=m;++j){
if(ro[j]==0){
ro[j]=-1;
aim=j;
break;
}
}
if(aim==-1)return puts("-1")*0;
int s0=re[aim].size();
for(int j=0;j<s0;++j){
ro[re[aim][j]]--;
}
ans[n++]=aim;
aim=-1;
}
return 1;
}
///用队列进行拓扑排序
int topo0(){
for(int i=1;i<=m+cc;++i)if(!ro[i])que.push(i);
int t0=1;
while(!que.empty()){
int x=que.front();
que.pop();
if(x<=m)ans[t0++]=x;
for(int i=0;i<re[x].size();++i){
int y=re[x][i];
ro[y]--;
if(!ro[y])que.push(y);
}
}
return t0>=m;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
//memset(er,0,sizeof(er));///不可偷懒!
for(int j=1;j<=m;++j){
scanf("%d",&a[j].v);a[j].x=j;
}
sort(a+1,a+m+1);
int sta=-1;
for(int j=1;j<=m;++j){
if(a[j].v==-1)continue;
if(j==1||a[j].v!=a[j-1].v)cc++;
re[m+cc].push_back(a[j].x);
re[a[j].x].push_back(m+cc+1);
ro[a[j].x]++;
ro[m+cc+1]++;
/*
///这种方法不可行
// if(a[j-1].v==-1)continue;
// if(a[j].v==a[j-1].v){
// if(sta<0)sta=j-1;
// if(er[a[j-1].x]==0)continue;
// ro[a[j].x]++;
// re[er[a[j-1].x]].push_back(a[j].x);
// er[a[j].x]=er[a[j-1].x];
// continue;
// }
// if(sta>0){
// for(int k=sta;k<j-1;++k){
// ro[a[j].x]++;
// re[a[k].x].push_back(a[j].x);
// }
// sta=-1;
// }
// ro[a[j].x]++;
// re[a[j-1].x].push_back(a[j].x);
// er[a[j].x]=a[j-1].x;
*/
}
cc++;
///必不可少,因为在无重复的元素组(有m个元素)中,需要m+1个辅助点(有一个充当了"头")
}
cout<<"cc: "<<cc<<endl;
for(int i=0;i<=m+cc;++i)cout<<ro[i]<<" ";
cout<<endl;
if(topo0()) for(int i=1;i<=m;++i)printf("%d ",ans[i]);else puts("-1");
}