[POJ 1636] Prison rearrangement

[POJ 1636] Prison rearrangement

[POJ 1636] Prison rearrangement

原题地址: http://acm.pku.edu.cn/JudgeOnline/problem?id=1636

题目大意:

拥有同样容量m的两个监狱打算交换一些犯人(<=m/2),但是有一些囚犯不可以放在同一个监狱里头。问最多可以交换多少人(再多也不能超过m/2)

题目分析:

不妨把监狱称为A和B。当存在牵连关系时,A中的某个犯人移动到B,会牵动B中一些犯人移动到A,如此反复……其实也就是如果要移动A,其最终结果是A中的一部分人和B中的一部分人整体交换。那么就需要求出这样的一个交换关系(传递闭包)。

思路:

用一个暴力的Floyd来解决传递闭包的问题,再用一个经典的二维背包来解决移动问题。其实算法都是很基础的,但是还是TLE了两次,原因在于对一些细节的不注意。

有些时候看似危险的复杂度,其实只要把一些细节考虑好了,就可以从TLE跨越到AC。尤其是常见的floyd,加了一行小小的连优化都不算的判断就从TLE变为AC了。

下面代码里头把原来TLE的部分也留在注释里,自己引以为戒。

代码:

 

POJ 1636
#include <cstdio>
#include 
<cstdlib>
#include 
<algorithm>
using namespace std;

const int maxn = 410;

int m, group, Afree, Bfree;
bool map[maxn][maxn];
int A_size[maxn], B_size[maxn];

void init(){
    
int i, r;
    scanf(
"%d%d"&m, &r);
    memset(map, 
0sizeof(bool)*maxn*maxn);
    
for (i=1; i<=2*m; i++)
        map[i][i] 
= 1;
    
for (i=1; i<=r; i++){
        
int a, b;
        scanf(
"%d%d"&a, &b);
        b 
+= m;                            //B监狱中的点编号自加m,方便floyd 
        map[a][b] = map[b][a] = 1;
    }

}


void floyd(){
    
int i,j,k;
    
for (j=1; j<=2*m; j++)
        
for (i=1; i<=2*m; i++)
            
if (map[i][j])                //  添加完这个,从TLE --> AC  important!!!!!
            for (k=1; k<=2*m; k++)
                map[i][k] 
= map[i][k] || (map[i][j] && map[j][k]);
}


void build(){
    
int i,j,k;
    
int color[maxn];
    group 
= 0;
    memset(color, 
0sizeof(int)*maxn);
    memset(A_size,  
0sizeof(int)*maxn);
    memset(B_size,  
0sizeof(int)*maxn);
    Afree 
= Bfree = 0;
    
for (i=1; i<=2*m; i++)                    //枚举所有点 
        if (color[i] == 0){                    //如果未染色 
            ++group;
            
for (j=1; j<=2*m; j++)
                
if (map[i][j]){                //如果属于一个大组(原来在一监狱需要一起移动,不在一个监狱则会冲突 
                    color[j] = group;
                    
if (j<=m)  A_size[group]++;
                    
else B_size[group]++;
                }

            
if (A_size[group] == 1 && B_size[group] == 0){
                color[i] 
= 0;
                Afree
++;
                A_size[group
--= 0;
            }

            
else
            
if (B_size[group] == 1 && A_size[group] == 0){
                color[i] 
= 0;
                Bfree
++;
                B_size[group
--= 0;
            }

        }

}


void solve(){
    
int i,j,k;
    
bool f[maxn][maxn];                        //f[i][j] == true 表示A移动i人B移动j人是可以的 
    memset(f, 0sizeof(bool)*maxn*maxn);
    
for (i=0; i<=Afree; i++)
        
for (j=0; j<=Bfree; j++)
            f[i][j] 
= 1;
//    for (i=0; i<=m; i++)
//        for (j=0; j<=m; j++)
//            for (k=1; k<=group; k++)        //枚举交换的组
//                if (i-A_size[k]>=0 && j-B_size[k]>=0 && f[i-A_size[k]][j-B_size[k]])
//                    f[i][j] = 1;
// 更换循环次序,也可以带来时间上的大大节省! 
    for (k=1; k<=group; k++)
        
for (i=m/2 - A_size[k]; i>=0; i--)
            
for (j=m/2 - B_size[k]; j>=0; j--)
                
if (f[i][j] && i+A_size[k]<=m/2 && j+B_size[k]<=m/2)
                    f[i
+A_size[k]][j+B_size[k]] = 1;
    
for (i=m/2; i>=0; i--)
        
if (f[i][i]) break;
    printf(
"%d\n", i);
}


int main(){
    
int T;
    scanf(
"%d"&T);
    
while (T--){
        init();
        floyd();
        build();
        solve();
    }

    system(
"pause");
    
return 0;
}

你可能感兴趣的:([POJ 1636] Prison rearrangement)