Topcoder SRM 358:SharksDinner
有一些鲨鱼要进行晚餐,他们要互相吃对方。每个对于都有一个尺寸size,速度speed,和智力intelligence。鲨鱼A能够吃鲨鱼B当且进当A的size,speed,intelligence都大于或者等于B鲨鱼的。并且每个鲨鱼最多能够吃2个其它的鲨鱼。
现在给定int[] size, int[] speed and int[] intelligence,要求出最后存活的鲨鱼的最小数量。
分析:要求最后存活的鲨鱼的最小数量,就要 求被吃的鲨鱼的最大数量。
假设有n个鲨鱼,我们建立一个二分图,每个鲨鱼有3个顶点Ax1,Ax2,Bx,其中2个顶点Ax1,Ax2在二分图的左图中,另外一个顶点Bx在二分图的右图中,如果有鲨鱼x能够吃鲨鱼y,我们就建立边Ax1->By,Ax2->By,这样,问题就转化为求这个二分图的最大匹配match,最后的结果就是n-match.
二分图的最大匹配使用匈牙利算法(寻找增广路)
code:
现在给定int[] size, int[] speed and int[] intelligence,要求出最后存活的鲨鱼的最小数量。
分析:要求最后存活的鲨鱼的最小数量,就要 求被吃的鲨鱼的最大数量。
假设有n个鲨鱼,我们建立一个二分图,每个鲨鱼有3个顶点Ax1,Ax2,Bx,其中2个顶点Ax1,Ax2在二分图的左图中,另外一个顶点Bx在二分图的右图中,如果有鲨鱼x能够吃鲨鱼y,我们就建立边Ax1->By,Ax2->By,这样,问题就转化为求这个二分图的最大匹配match,最后的结果就是n-match.
二分图的最大匹配使用匈牙利算法(寻找增广路)
code:
#include <iostream>
#include <vector>
using namespace std;
struct st {
int a[ 3 ];
st( ) {
for( int i=0;i<3;i++ ) a[ i ]=0;
}
st( int A,int B,int C ) {
a[ 0 ]=A;
a[ 1 ]=B;
a[ 2 ]=C;
}
};
const int MAXN=250;
bool eq( const st& x,const st& y ) {
for( int i=0;i<3;i++ )
if( x.a[ i ]!=y.a[ i ] )
return false;
}
//判断鲨鱼x是否能够吃鲨鱼y
bool can_eat( const st& x,const st& y ) {
if( eq( x,y ) ) return false;
for( int i=0;i<3;i++ )
if( x.a[ i ]<y.a[ i ] ) return false;
return true;
}
st a[ MAXN ]; //保存鲨鱼信息
bool gr[ MAXN ][ MAXN ]; //二分图
int n,m; //二分图两个子图的节点数
int match[ MAXN ]; //记录二分图的匹配信息
bool us[ MAXN ]; //记录二分图第二个子图中的节点是否用过
//从二分图的第一个子图中的v节点开始寻找增广路径
bool go( int v ) {
for( int i=0;i<m;i++ ) {
if( us[ i ] ) continue;
if( gr[ v ][ i ] ) {
if( match[ i ]==-1 ) {
us[ i ]=1;
match[ i ]=v;
return true;
}
}
}
for( int i=0;i<m;i++ ) {
if( us[ i ] ) continue;
if( gr[ v ][ i ] ) {
us[ i ]=1;
if( go( match[ i ] ) ) {
match[ i ]=v;
return true;
}
}
}
return false;
}
//进行最大二分匹配
int matcher( ) {
int res=0;
memset( match,255,sizeof match );
//进行n此增广路操作
for( int i=0;i<n;i++ ) {
memset( us,0,sizeof us );
if( go( i ) ) ++res;
}
return res;
}
class SharksDinner {
public:
static int minSurvivors( vector<int> A,vector<int> B,vector<int> C ) {
n=A.size( );
memset( gr,0,sizeof( gr ) );
for( int i=0;i<n;i++ )
a[ i ]=st( A[ i ],B[ i ],C[ i ] );
//构造二分图
for( int i=0;i<n;i++ ) {
for( int j=0;j<n;j++ ) {
if( can_eat( a[ i ],a[ j ] ) ) {
gr[ 2*i ][ j ]=1;
gr[ 2*i+1 ][ j ]=1;
}
}
for( int j=0;j<i;j++ ) {
if( eq( a[ i ],a[ j ] ) ) {
gr[ 2*i ][ j ]=1;
gr[ 2*i+1 ][ j ]=1;
}
}
}
n=2*A.size( );
m=A.size( );
return m-matcher( );
}
};
#include <vector>
using namespace std;
struct st {
int a[ 3 ];
st( ) {
for( int i=0;i<3;i++ ) a[ i ]=0;
}
st( int A,int B,int C ) {
a[ 0 ]=A;
a[ 1 ]=B;
a[ 2 ]=C;
}
};
const int MAXN=250;
bool eq( const st& x,const st& y ) {
for( int i=0;i<3;i++ )
if( x.a[ i ]!=y.a[ i ] )
return false;
}
//判断鲨鱼x是否能够吃鲨鱼y
bool can_eat( const st& x,const st& y ) {
if( eq( x,y ) ) return false;
for( int i=0;i<3;i++ )
if( x.a[ i ]<y.a[ i ] ) return false;
return true;
}
st a[ MAXN ]; //保存鲨鱼信息
bool gr[ MAXN ][ MAXN ]; //二分图
int n,m; //二分图两个子图的节点数
int match[ MAXN ]; //记录二分图的匹配信息
bool us[ MAXN ]; //记录二分图第二个子图中的节点是否用过
//从二分图的第一个子图中的v节点开始寻找增广路径
bool go( int v ) {
for( int i=0;i<m;i++ ) {
if( us[ i ] ) continue;
if( gr[ v ][ i ] ) {
if( match[ i ]==-1 ) {
us[ i ]=1;
match[ i ]=v;
return true;
}
}
}
for( int i=0;i<m;i++ ) {
if( us[ i ] ) continue;
if( gr[ v ][ i ] ) {
us[ i ]=1;
if( go( match[ i ] ) ) {
match[ i ]=v;
return true;
}
}
}
return false;
}
//进行最大二分匹配
int matcher( ) {
int res=0;
memset( match,255,sizeof match );
//进行n此增广路操作
for( int i=0;i<n;i++ ) {
memset( us,0,sizeof us );
if( go( i ) ) ++res;
}
return res;
}
class SharksDinner {
public:
static int minSurvivors( vector<int> A,vector<int> B,vector<int> C ) {
n=A.size( );
memset( gr,0,sizeof( gr ) );
for( int i=0;i<n;i++ )
a[ i ]=st( A[ i ],B[ i ],C[ i ] );
//构造二分图
for( int i=0;i<n;i++ ) {
for( int j=0;j<n;j++ ) {
if( can_eat( a[ i ],a[ j ] ) ) {
gr[ 2*i ][ j ]=1;
gr[ 2*i+1 ][ j ]=1;
}
}
for( int j=0;j<i;j++ ) {
if( eq( a[ i ],a[ j ] ) ) {
gr[ 2*i ][ j ]=1;
gr[ 2*i+1 ][ j ]=1;
}
}
}
n=2*A.size( );
m=A.size( );
return m-matcher( );
}
};