uva1627(模型转换01背包)

题意:有n(n<=100)个人,把他们分成非空的两组,使得每个人都被分到一组,且同组中的人相互认识。要求两组的成员人数尽量接近。多解时输出任意方案,无解时输出No Solution。


解法:如果两人不是相互都认识,就连一条无向边。所以就变成了一个无向图。对于每一个连通分量来说,必须是个二分染色图,否则就No solution。然后对于每个联通分量,黑白两种颜色有个数量差,用所有的差来进行01背包,找到距离集合差0最小的方案。


代码:

/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=110;
const LL INF=0x3FFFFFFF;
int n;
int rem[Max][Max];
vector<int> dp;
vector<int> vec[Max];
int vis[Max];
int fin[Max];
int sad[Max];
int dfs(int t,int y,int cas)
{
    vis[t]=cas;
    if(fin[t]==y)
        return 0;
    if(fin[t]!=-1)
        return -1;
    fin[t]=y;
    sad[t]=y;
    for(int i=0; i<vec[t].size(); i++)
        if(dfs(vec[t][i],y^1,cas)==-1)
            return -1;
    return 0;
}
int ans[Max][Max*3];
int out[Max][Max*3];
int be[Max][Max*3];
int main()
{
    //freopen ("in.txt" , "r" , stdin);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1; i<=n; i++)
            vec[i].clear();
        memset(vis,-1,sizeof vis);
        dp.clear();
        memset(rem,0,sizeof rem);
        for(int i=1; i<=n; i++)
        {
            int c;
            while(scanf("%d",&c)==1&&c)
            {
                rem[i][c]=1;
            }
            rem[i][i]=1;
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                if((!rem[i][j])||(!rem[j][i]))
                    vec[i].push_back(j);
        }
        bool flag=0;
        int cas=0;
        memset(sad,0,sizeof sad);
        for(int i=1; i<=n; i++)
        {
            memset(fin,-1,sizeof fin);
            if(vis[i]!=-1)
                continue;
            if(dfs(i,0,cas++)==-1)
            {
                flag=1;
                break;
            }
            int t1=0, t2=0;
            for(int i=1; i<=n; i++)
                if(fin[i]==0)
                    t1++;
                else if(fin[i]==1)
                    t2++;
            dp.push_back(t1-t2);
        }
        if(flag)
        {
            puts("No solution");
             if(t>0)
        cout<<endl;
            continue;
        }
        memset(ans,0,sizeof ans);
        memset(be,0,sizeof be);
        ans[0][100+dp[0]]=1;
        out[0][100+dp[0]]=0;
        ans[0][100-dp[0]]=1;
        out[0][100-dp[0]]=1;
        for(int i=1; i<dp.size(); i++)
        {
            for(int j=0; j<=100*2; j++)
            {
                if(ans[i-1][j])
                {
                    ans[i][j+dp[i]]=1;
                    out[i][j+dp[i]]=0;
                    be[i][j+dp[i]]=j;
                    ans[i][j-dp[i]]=1;
                    out[i][j-dp[i]]=1;
                    be[i][j-dp[i]]=j;
                }
            }
        }
        int m=dp.size();
        vector<int> ans0;
        vector<int> ans1;
        for(int i=0; i<=100; i++)
        {
            if(ans[m-1][100-i]==1||ans[m-1][100+i]==1)
            {
                int num;
                if(ans[m-1][100-i]==1)
                    num=100-i;
                else
                    num=100+i;
                for(int k=m-1; k>=0; k--)
                {
                    for(int i=1; i<=n; i++)
                    {
                        if(vis[i]==k&&sad[i]==out[k][num])
                            ans0.push_back(i);
                        if(vis[i]==k&&sad[i]==1-out[k][num])
                            ans1.push_back(i);
                    }
                    num=be[k][num];
                }
                break;
            }
        }
        cout<<ans0.size();
        for(int i=0; i<ans0.size(); i++)
            cout<<" "<<ans0[i];
        cout<<endl;
        cout<<ans1.size();
        for(int i=0; i<ans1.size(); i++)
            cout<<" "<<ans1[i];
        cout<<endl;
        if(t>0)
        cout<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值