Team them up! UVA - 1627(dp+二分图+打印路径+连通分量)

题目链接:点击打开链接

题目大意: 给出n个人,每个人都有认识的人,然后是否能分成两组,每组的人都相互认识,求两组最小人数差,不过不能分成两组,输出no solvnion。

题目思路:一开始是挺没有思路的,如果仔细分析的话可以分析出,对于,每一个不相互认识的人可以看成二分图,因为只能分成两种人,一种相互认识,一种不相互认识,

所以的话,我们把不相互认识的建图,有边存在的一定不能放在同一个集合中,这样的话构建出来的图是一个一个联通块,对于每一个联通块来说,如果不是二分图,肯定是不合法的,判定是否合法之后,我们的问题就是怎么找到差值最小的安排方式,经过前面的分析,我们可以发现每一个联通块分为两个绑定的团体,不同联通块则没有关系,这样的话,我们可以想到用dp去解决,设状态dp[i][j],为前i个联通块,第一个比第二个多j个是否存在,为什么呢,一般我们都是设差的绝对值,这里因为要打印路径,我们设为这样,为了方便打印路径,或者设为dp[i][j][k],为前i个联通块中第一个为j个,第二个为k个,剩下的问题就是打印路径了,因为每一个联通块的两种人只能选择是去第一个或则者去第二个,这样的话,给我们打印路径提供了很大的方便,从最后的答案逆推回去,根据这两种选择看上一个上一个状态是否存在,然后记录下来就行了。

ac代码;

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstring>
#include<iostream>
#include<sstream>
#include<cmath>
#include<vector>
#define LL long long
#define INF 0x3f3f3f3f
#define eps 1e-6
using namespace std;
int T;
int mp[105][105];
int head[105];
int tot;
int n;
int cntt;
int color[105];
int diff[105];
int dp[105][205];
vector<int>G[105][2];
struct node
{
    int u,v;
    int net;
}E[105*105];
int vis[105];
void __init__()
{
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(mp,0,sizeof(mp));
    memset(color,-1,sizeof(color));
    memset(diff,0,sizeof(diff));
    memset(dp,0,sizeof(dp));
    tot = 0;
    cntt = 0;
    for(int i = 0;i<n;i++){
        G[i][0].clear();
        G[i][1].clear();
    }
}
void build(int u,int v)
{
    E[tot].u =u;
    E[tot].v = v;
    E[tot].net = head[u];
    head[u] = tot++;
}
int dfs(int u,int clo)
{
    color[u] = clo;
    vis[u] = 1;
    G[cntt][clo].push_back(u);
    for(int i = head[u];~i;i = E[i].net){
        int to = E[i].v;
        if(u==to)
            continue;
        if(color[to]==color[u])
            return 0;
        if(color[to]==-1&&!dfs(to,clo^1))
            return 0;
    }
    return 1;
}
void print(int ans)
{
    vector<int>ans1;
    vector<int>ans2;
    for(int i =cntt-1;i>=0;i--){
            int t;
        if(dp[i][ans-diff[i]+n]){
            t = 0;
            ans-=diff[i];
        }
        else{
            t = 1;
            ans+=diff[i];
        }
        int len = G[i][t].size();
            for(int j = 0;j<len;j++)
            ans1.push_back(G[i][t][j]);
             len = G[i][t^1].size();
            for(int j = 0;j<len;j++){
                ans2.push_back(G[i][t^1][j]);
            }
    }
    int len1 = ans1.size();
    int len2 = ans2.size();
    if(len1==0||len2==0){
        puts("No solution");
        return ;
    }
    cout<<len1<<' ';
    for(int i = 0;i<len1;i++)
    {
        printf("%d%c",ans1[i],i==len1-1?'\n':' ');
    }
    cout<<len2<<' ';
    for(int i = 0;i<len2;i++){
         printf("%d%c",ans2[i],i==len2-1?'\n':' ');
    }
}
void dpp()
{
    dp[0][n] = 1;
    for(int i = 0;i<cntt;i++){
        for(int j = -n;j<=n;j++)
        {
            if(dp[i][j+n]){
                dp[i+1][j-diff[i]+n] = 1;
                dp[i+1][j+diff[i]+n] = 1;
            }
        }
    }
    for(int i = 0;i<=n;i++){
        if(dp[cntt][i+n]){
            print(i);
            return ;
        }
        if(dp[cntt][-i+n]){
            print(i);
            return ;
        }
    }
}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        __init__();
        for(int i = 1;i<=n;i++){
            while(1){
                int x;
                scanf("%d",&x);
                if(!x)
                    break;
                mp[i][x] = 1;
            }
        }
        for(int i = 1;i<=n;i++){
            for(int j = i+1;j<=n;j++){
                    if(mp[i][j]==0||mp[j][i]==0)
                    {
                        build(i,j);
                        build(j,i);
                    }
            }
        }
        int falg = 0;
        for(int i = 1;i<=n;i++){
            if(vis[i]==0&&color[i]==-1){
                if(!dfs(i,0)){
                    falg = 1;
                    break;
                }
                else
                {
                    diff[cntt] = G[cntt][0].size()-G[cntt][1].size();
                    cntt++;
                }
            }
        }
        if(falg)
            puts("No solution");
        else{
            dpp();
        }
        if(T)
            puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值