POJ 1636 Prison rearrangement 二部圖連通分量+揹包

以第三組爲例,我們根據輸入可以得到這個二部圖

在這裏插入圖片描述

根據不能放在一起的情況可以得到這樣的連通分量
對於每一個連通分量,我們將這個連通分量按照監獄分爲兩個部分
這兩個部分調整的時候要作爲整體來調整。
**Eg:**我們假設將一號監獄的5調到二號監獄,則二號監獄的5要到一號監獄,隨之一號監獄的2,3,4也要調到二號監獄。

所以
我們找到所有的連通分量,按照監獄各自分爲兩個部分,視爲兩個物品
②運用揹包模型,對物品進行調整
轉態轉移

dp[j][k]表示,存在方案使得第一個監獄出j個人,第二個監獄出k個人的調整,j k不一定相等

對於第i個連通分量,第一個監獄對應p1[i]個人,第二個監獄對應p2[i]個人

if( dp[ j-p1[i] ] [ k-p2[i] ] == 1 ){
    dp[j][k] = 1;
}

則有如上關係。
注意由於每一個物品只能使用一次,並且是恰好填滿的情況,
所有遍歷順序是從下至上,從右至左

③遍歷dp數組,取得dp[i][i]==1的時候,最大的i值即爲答案。
表示一號監獄與二號監獄拿出的人數一樣多,切有這樣的方案。

#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <vector>
#include <cstdio>

using namespace std;

#define debug(x) cout<<#x<<": "<<x<<endl;

int n,m,r;

int p1[201];
int p2[201];
vector<int> danp[401];

bool visit[402];

bool dp[101][101];

int pcnt = 0;
int xi=0;
int yi=0;

void dfs(int a){
    //debug(n)
    visit[a] = 1;
    if( a<=m ){
        p1[pcnt] ++;
    }else{
        p2[pcnt] ++;
    }
    vector<int>&v = danp[a];
    for(int i=0;i < v.size();i++){
        if( visit[ v[i] ] == 0 ){
            dfs(v[i]);
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int nn = 0;nn < n; nn ++){
        scanf("%d%d",&m,&r);

        for(int i=0;i<= m*2;i++){
            danp[i].clear();
        }

        memset(visit,0,sizeof(visit));
        memset(p1,0,sizeof(p1));
        memset(p2,0,sizeof(p2));
        memset(dp,0,sizeof(dp));



        pcnt = 0;
        //debug(000)

        for(int rr=0;rr<r;rr++){

            scanf("%d%d",&xi,&yi);
            danp[xi].push_back(m+yi);
            danp[m+yi].push_back(xi);
        }

        //debug(111)

        for(int i=1;i <= 2*m;i ++){
            if( visit[i] == 0 ){
                dfs(i);
                pcnt++;

            }
        }

        int ret = 0;
        dp[0][0] = 1;
        for(int i = 0;i < pcnt;i++){
            for(int j = m/2;j >=p1[i] ;j--){
                for(int k = m/2;k >= p2[i];k--){
                    if( dp[ j-p1[i] ] [ k-p2[i] ] == 1 ){
                        dp[j][k] = 1;
                    }
                }
            }
        }
        for(int i = m/2;i >=0;i--){
            if( dp[i][i] == 1 ){
                ret = i;
                break;
            }
        }

        cout<<ret<<endl;
    }
    return 0;
}


在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章