POJ 1033 Defragment

根据http://hi.baidu.com/algorithm/item/d51b15f7a8ea1c0a84d278be这个开始练习ac,刚开始接触这道题时以为是道搜索题,读完之后深思了一下,感觉不需要套用一贯的dfs或者bfs,直接根据自己的思路走,然后注意一下效率问题就行了!可见算法注重灵活,而不是一贯的套用现有的模式。


利用题中示例:

                                        簇号from[]:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
     目前簇中内容应该在的簇号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[]指在优化之后,簇i存放的是原来的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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值