做这道题的第一个障碍在于读题。刚开始把题目读了三遍,也没搞清楚要求什么。其实可以把shape和decoration看成点,它们之际的对应关系看成边,这样就得到一个图。用A表示shape的集合,B表示decoration的集合。题目要求的就是原图的一个最大子图:使得该子图也可以分为A’和B’两部分,且从A’的每个点出发,到B’的任意点都存在边。
该题的思路:枚举+暴搜。即:枚举K值,对每个k值进行DFS搜索。那么,数据的规模有多大?暴搜是否可行?设最大子图的A’和B’集合各有K个点,则最大子图共有K*K条边。现在原图共有m条边,且m<100。显然,MAX( k ) <= sqrt( 100 ) = 10,也就是说数据规模很小,暴搜可行。
具体的搜索过程:依次检测A集合中的每个点,判断该点是否在A’中。设当前检测的点为t,则存在两种情况:t在A’中,t不在A’。对这两种情况,再分别递归调用DFS。设点t已经在A’中,且v1, v2,…vn分别为集合B中与点t有对应关系的点。根据题目,下一个点t+1也在A’中的条件是:v1, v2,…vn同样也是B中与t+1有对应关系的点。
在具体编程实现的时候,需要存储shape和decoration的对应关系。当然,第一反应是用一个二维数组。但是,仔细想想:要判断t+1的对应点同样也是t的对应点,用位运算&应该更容易,因此可以用bitset存储shape和decoration的对应关系。
#include <iostream> #include <bitset> using namespace std; //***********************常量定义***************************** const int MAX_NUM = 38; //*********************自定义数据结构************************* //********************题目描述中的变量************************ int caseNum; int pairNum; //**********************算法中的变量************************** //存储从shape到decration的映射关系 bitset<MAX_NUM> graph[MAX_NUM]; //***********************算法实现***************************** //totalNum指当前枚举的K值 //curId指当前检测的shape编号,从1到36依次检测每个shape //curNum指当前已有的shape数 //maskz值当前的掩码 bool DFS( int totalNum, int curId, int curNum, bitset<MAX_NUM> mask ) { //必须先检测上次选中的shape是否可行 if( mask.count() < totalNum ) return false; //如果可行,且数目已足够 if( curNum == totalNum ) return true; //剪枝 //当加上剩余所有的shape都不够totalNum时, 直接返回,不再搜索 if( curNum + 36 - curId < totalNum ) return false; //对当前编号的shape有两种选择 //curNum: 不选当前shape,curId+1: 检测下一个 //curNum+1: 选当前shape,mask&graph[curId]: 修改mask return ( DFS( totalNum, curId+1, curNum, mask ) || DFS( totalNum, curId+1, curNum+1, mask&graph[curId] ) ); } void Init() { for( int i=0; i<MAX_NUM; i++ ) { graph[i].reset(); } } void Input() { cin >> pairNum; for( int j=0; j<pairNum; j++ ) { int shape, decr; //输入shape、decration的编号,编号范围从1...36 //将编号范围转换到0...35 cin >> shape >> decr; graph[shape-1].set( decr-1 ); } } void Solve() { bitset<MAX_NUM> mask; //把mask的所有位都置1 mask.set(); int k; //从小到大枚举k, 对每个k值用DFS进行搜索 for( k=2; k*k<=pairNum; k++ ) { if( !DFS( k, 0, 0, mask ) ) break; } cout << k-1 << endl; } //************************main函数**************************** int main() { freopen( "in.txt", "r", stdin ); cin >> caseNum; while( caseNum-- ) { Init(); Input(); Solve(); } return 0; }