根据http://hi.baidu.com/algorithm/item/d51b15f7a8ea1c0a84d278be这个开始练习ac,刚开始接触这道题时以为是道搜索题,读完之后深思了一下,感觉不需要套用一贯的dfs或者bfs,直接根据自己的思路走,然后注意一下效率问题就行了!可见算法注重灵活,而不是一贯的套用现有的模式。
利用题中示例:
目前簇中内容应该在的簇号to[]:0 1 2 0 7 0 5 0 0 8 3 4 0 0 0 0 0 6 0 0
很自然的想法就是同时遍历from和to数组,如果from和to中内容相同,说明当前簇中内容不需要移动,否则为了优化必须进行移动,但是可以移动的前提是to所指的簇中内容为空,即from[to[i]]=0。可以先把满足移动条件的进行移动,由于移动之后,可能会引起其它簇满足移动条件,所以还需要再次遍历from和to,直到没有满足移动的簇。这个时候,有两种情况,一是达到优化状态,即from=to;另外一种是存在一个环,就如题中的5-7,7-5。针对第二种情况就是打破环,然后再次对满足移动条件的簇进行移动。多个嵌套循环就能解决了,但是这个过程使得需要多次遍历才能把所有满足移动条件的簇操作完全(由于移动导致了其他一些簇满足了移动条件后,需要第二次遍历才能将这些簇进行移动)。很明显会超时的。
有没有可能使得一次遍历之后,就能把不属于环的一些簇全部移动到位呢?答案是肯定的。
看下面代码,opt[]指在优化之后,
存放的是原来的 opt[i]的内容。
#include <iostream> #include <vector> using namespace std; int n, k; vector<int> opt ; vector<int> c2f; int count; bool moved = false; void MoveBeginAt(int pos) { while( opt[pos] != 0 && !c2f[pos] ) { int from = opt[pos]; int to = pos; cout << from << " " << to << endl; moved = true; c2f[to] = c2f[from]; c2f[from] = 0; opt[pos] = pos; pos = from; } } int MaxFreeIndex() { int i = c2f.size(); while( c2f[--i] ); return i; } int main() { while( cin >> n >> k ) { opt.assign(n+1, 0); c2f.assign(n+1, 0); count = 0; moved = false; for(int i = 0; i < k; ++i) { int m; cin >> m; for(int j = 0; j < m; ++j) { int c; cin >> c; opt[++count] = c; c2f[c] = i + 1; } } for(int i = 1; i <= count; ++i) if( !c2f[i] ) MoveBeginAt( i ); int from = 0 ; for(int i = 1; i <= count; ++i) { if( opt[i] != i ) { if( !from ) from = i; else if( opt[i] == from ) { int to = MaxFreeIndex(); cout << from << " " << to << endl; opt[i] = to; c2f[to] = c2f[from]; c2f[from] = 0; MoveBeginAt( from ); i = from ; from = 0; } } } cout << (moved?"":"No optimization needed\n") ; } return 0; }
另外还有一种利用栈的解法,也很巧妙,思路见 http://www.cnblogs.com/damacheng/archive/2010/09/24/1833983.html;代码实现如下:
#include <iostream> #include <stack> #include <vector> using namespace std; int n, k; vector<int> opt; stack<int> s; int count ; int MaxFreeIndex() { int i = opt.size() - 1; while( opt[i] ) --i; return i; } void Work() { bool moved = false; for(int i = 1; i < opt.size(); ++i){ if( opt[i] && opt[i] != i ){ moved = true; s.push( i ); int b = i, cur = opt[i]; while( true ){ s.push( cur ); if( !opt[cur] ) break; else if( opt[cur] == b ){ int j = MaxFreeIndex(); cout << cur << " " << j << endl; opt[j] = b; opt[cur] = 0; break; } cur = opt[cur]; } int from, to = s.top(); s.pop(); while( !s.empty() ){ from = s.top(); cout << from << " " << to << endl; opt[to] = opt[from]; to = s.top(); s.pop(); } opt[to] = 0; } } cout << (moved?"":"No optimization needed\n") ; } int main() { while( cin >> n >> k ){ opt.assign( n + 1, 0 ); count = 0; for(int i = 0; i < k; ++i){ int m; cin >> m; for(int j = 0; j < m; ++j){ int c; cin >> c; opt[c] = ++count; } } Work(); } return 0; }