TopCoder SRM 298 Div1 Medium OrderDoesMatter

問題

n個の行列が与えられて、それぞれの大きさはN[i]行M[i]列である。
この行列を、好きな順に並び替えて掛け算する。
(ただし、並び替えた順で積が定義されるようにしなけらばならない)


このとき、積の行列の要素の数がもっとも多くなるような並び順について、
積の行列の要素の数を求めよ。
積が定義できる並び順が存在しないとき、-1を返せ。

制約条件

n≦50
N[i],M[i]≦1000

方針

行または列の大きさの整数を頂点とするようなグラフを考える。
n行m列の行列があるとき、nからmへの有向辺を張る。


このようにして出来たグラフの、オイラー路が行列の積に対応する。
オイラー路が存在することが必要、すなわち、

  • グラフが連結かつ以下のどちらかがなりたつ
  • 全ての頂点の相対次数が0
  • ひとつの頂点の相対次数が+1, もうひとつの頂点の相対次数が-1, その他全ての頂点の相対次数が0

が成り立たなければ-1.


オイラー路があるときは、
全ての次数が0だったら、好きな頂点を始点に出来るので、最も大きい数字頂点をスタートに選べばよい。
そうでない場合、オイラー路は一通りになるので、積も一通りになる。

ソースコード

bool e[1001][1001], v[1001];
int deg[1001];

struct OrderDoesMatter{
	int getOrder(vector<int> N, vector<int> M){
		int n=N.size();
		for(int i=0;i<n;i++){
			e[N[i]][M[i]]=1;
			deg[N[i]]++;
			deg[M[i]]--;
		}
		int nz=0, pi=N[0], mi=N[0];
		for(int i=0;i<=1000;i++)if(deg[i]!=0){
			nz++;
			if(abs(deg[i])!=1)return -1;
			if(deg[i]<0)mi=i;
			if(deg[i]>0)pi=i;
		}
		if(nz>2)return -1;
		rec(pi);
		for(int i=0;i<n;i++)if(!v[N[i]])return -1;
		
		if(nz)return mi*pi;
		int ans=0;
		for(int i=0;i<n;i++)ans=max(ans,N[i]*N[i]);
		return ans;
	}
	
	void rec(int c){
		v[c]=1;
		for(int i=0;i<=1000;i++)if(e[c][i]&&!v[i])rec(i);
	}
};