problem link http://codeforces.com/contest/117/problem/C
This problem needs to find 3-length ring in a type of graph call tournament
One important precondition of this problem is that :
A tournament is a directed graph without self-loops in which every pair of vertexes is connected by exactly one directed edge. That is, for any two vertexes u and v (u ≠ v) exists either an edge going from u to v, or an edge from v to u
My solution gets Accepted, but runs more than 2 times slower than the fastest solution.
First tall about my solution, it's simple, using DFS to find ring.
Remind that in DFS, if the search path reaches a vertex with color gray(means this vertex has been started DFS, but hasn't finished), then a ring occur, moreover, for every vertex in the search path, if the vertex v's deep level is bigger than that gray vertex, then v is inside this ring. )
For every vertex in the DFS process, keep tracing the minimun length ring that it is in, if found the minimun ring which length is 3, then answer is near, by this time, 2 of the 3 vertex is known( in the DFS process, current visited vertex and the next visited vertex are always known), so just need to find the 3th vertex vx that satisfy G[vx][v1] == 1 && G[v2][vx] == 1.
#include <stdio.h>
#define MAX 5001
#define WHITE 0
#define GRAY 1
#define BLACK 2
int G[MAX][MAX] = { 0 };
int v_stack[MAX] = { 0 };
int e_stack[MAX] = { 0 };
int deep[MAX ] = { 0 };
int v_color[MAX] = { 0 };
int min_ring_dest[MAX] = { 0 };
int min_ring_len[MAX] = { 0 };
int N;
char line[MAX] = { 0 };
int r1, r2, r3;
void find_3th(int v1, int v2) {
r1 = v1;
r2 = v2;
int i, j;
for( i=1;i<=N;i++ ) {
if( 1 == G[v2][i] && 1 == G[i][v1] ) {
r3 = i;
break;
}
}
}
bool dfs(int v) {
int stack_cnt = 0, i, ring_len, e, d;
v_stack[stack_cnt] = v;
e_stack[stack_cnt++] = 1;
v_color[v] = GRAY;
while( 0 != stack_cnt ) {
e = e_stack[stack_cnt-1];
v = v_stack[stack_cnt-1];
for( i=e;i<=N;i++ ) {
if( 1 == G[v][i] ) {
if( WHITE == v_color[i] ) { // unvisited vertex, add to stack ( search path )
e_stack[stack_cnt-1] = i; // next time find arc start from i+1, but need to check i for ring length before.
v_stack[stack_cnt] = i;
e_stack[stack_cnt++] = 1;
v_color[i] = GRAY;
deep[i] = deep[v]+1;
break;
}
else if ( GRAY == v_color[i] ) { // ring.
ring_len = deep[v] + 1 - deep[i];
if( 0 == min_ring_len[v] || ring_len < min_ring_len[v] ) { // update
min_ring_len[v] = ring_len;
if( 0 == min_ring_len[i] ) // i doesn't know himself inside a ring.
min_ring_dest[v] = i;
else
min_ring_dest[v] = min_ring_dest[i];
}
if( 3 == ring_len ) {
find_3th(v, i);
return true;
}
}
else { // if ( BLACK == v_color[i] )
if( min_ring_len[i] > 0 ) { // i inside a ring.
d = min_ring_dest[i];
if( deep[d] <= deep[v] && GRAY == v_color[d] ) { // oh, the terminal vertex of i's ring is v's ancesstor or even v itself.
ring_len = min_ring_len[i] - ( deep[i] - ( deep[v] + 1 ) ); // reduce the i's ring len.
if( 0 == min_ring_len[v] || ring_len < min_ring_len[v] ) {
min_ring_len[v] = ring_len;
min_ring_dest[v] = d;
}
if( 3 == ring_len ) {
find_3th(v, i);
return true;
}
}
}
}
}
}
if( i == N + 1 ) { // v adds no arc to stack, means v need to be black
v_color[v] = BLACK;
stack_cnt--;
}
}
return false;
}
int main() {
scanf("%d", &N);
int i,j,k;
for( i=1;i<=N;i++ ) {
scanf("%s", line);
for( j=0;j<N;j++ ) {
if( '1' == line[j] ) {
G[i][j+1] = 1;
}
}
}
for( i=1;i<=N;i++ ) {
if( WHITE == v_color[i] ) {
if( true == dfs(i) ) {
printf("%d %d %d\n", r1, r2, r3);
return 0;
}
}
}
printf("-1\n");
return 0;
}
My solution needs to record too many information that isn't necessary for this problem.
Here's the fastest solution in codeforces, author: syboy.
syboy's solution is much efficient、grace and appropriate for this problem.
Base on the definition of tournament graph, for every vertex v in graph, the rest vertexes could be devided into exactly two sets-- vertexes emanate v (S1) and vertexes emanated from v (S2).
So for v, if any vertex v1 in S2 can reach any vertex v2 in S1, then a 3 length ring is form.
If there's no such (v1, v2), then recursively do the same division for the subgraph S1, S2 respectively, until target ring is found.
Here is his code
//BE NAMASH
#include<iostream>
#include<stdio.h>
using namespace std;
const int MN=5100;
bool a[MN][MN];
short int A,B,C,l[MN],t[MN];
int n;
int find(int p,int q){
if(q-p<=2){
return 0;
}
int ef1=p+1,ef2=q-1;
int v=l[p];
t[p]=l[p];
for(int i=p+1;i<q;i++){
if(a[v][l[i]]){
t[ef2--]=l[i];
}else{
t[ef1++]=l[i];
}
}
for(int i=p+1;i<ef1;i++){
int v2=t[i];
for(int j=ef2+1;j<q;j++){
if(a[t[j]][v2]){
A=v;
B=t[j];
C=t[i];
return 1;
}
}
}
for(int i=p;i<q;i++){
l[i]=t[i];
}
if(find(p+1,ef1)||find(ef2+1,q)){
return 1;
}return 0;
}
char inp[50000000];
int main(){
// scanf("%d",&n);
cin>>n;
cin.read(inp,49999999);
int st=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
while(inp[st]!='0'&&inp[st]!='1'){
st++;
}
a[i][j]=inp[st]-'0';
st++;
}
l[i]=i;
}
if(find(0,n)){
cout<<A+1<<" "<<B+1<<" "<<C+1<<endl;
}else{
cout<<"-1"<<endl;
}return 0;
}