zoukankan      html  css  js  c++  java
  • POJ1112 Team Them Up![二分图染色 补图 01背包]

    Team Them Up!
    Time Limit: 1000MS   Memory Limit: 10000K
    Total Submissions: 7608   Accepted: 2041   Special Judge

    Description

    Your task is to divide a number of persons into two teams, in such a way, that: 

    everyone belongs to one of the teams; 

    every team has at least one member; 

    every person in the team knows every other person in his team; 

    teams are as close in their sizes as possible. 

    This task may have many solutions. You are to find and output any solution, or to report that the solution does not exist.

    Input

    For simplicity, all persons are assigned a unique integer identifier from 1 to N. 

    The first line in the input file contains a single integer number N (2 <= N <= 100) - the total number of persons to divide into teams, followed by N lines - one line per person in ascending order of their identifiers. Each line contains the list of distinct numbers Aij (1 <= Aij <= N, Aij != i) separated by spaces. The list represents identifiers of persons that ith person knows. The list is terminated by 0.

    Output

    If the solution to the problem does not exist, then write a single message "No solution" (without quotes) to the output file. Otherwise write a solution on two lines. On the first line of the output file write the number of persons in the first team, followed by the identifiers of persons in the first team, placing one space before each identifier. On the second line describe the second team in the same way. You may write teams and identifiers of persons in a team in any order.

    Sample Input

    5
    2 3 5 0
    1 4 5 3 0
    1 2 5 0
    1 2 3 0
    4 3 2 1 0

    Sample Output

    3 1 3 5
    2 2 4

    Source


    题意:

    白书

    一个N个节点的有向图,将节点分成两个集合,满足以下四个条件:

    1。每个节点属于其中一个集合

    2。每个集合至少有一个节点

    3。集合里的每一个节点都有边连向同一个集合里的其他点

    4。被分成的两个集合的大小要尽量接近


    如果两人不互相认识,则一定要被分在不同集合里
    所以建原图的补图,也就是两个不互相认识的人连一条边,求连通分量
    对每个cc二染色,失败的话No solution,否则相同颜色的人一定加在一个集合里
    每个cc相当于物品,原题就是求一些物品分两组,使他们的差值最小,01背包
     
    让cc的权值为两个颜色的人数之差
    f[i][j+n]表示前i个cc,第一组比第二组多j是否可行
    普通的状态方程和更新写法都可以,打印路径也没必要记录pa,用状态转移方程判断就可以
    也可以装一个N/2的背包
     
    PS:1.一定注意第二维+n
      2.POJ坑人数组越界爆WA
      3.变量一大片记不过来系列
    //更新写法
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    const int N=105;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,g[N][N];
    struct edge{
        int v,ne;
    }e[N*N<<1];
    int h[N],cnt=0;
    void ins(int u,int v){
        cnt++;
        e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
        cnt++;
        e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
    }
    int col[N],cc=0,tm[N][3][N],p[N][3];//team  p
    bool dfs(int u,int c){
        col[u]=c;
        tm[cc][c][++p[cc][c]]=u;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(col[u]==col[v]) return false;
            if(!col[v]&&!dfs(v,3-c)) return false;
        }
        return true;
    }
    int w[N];
    bool init(){
        for(int i=1;i<=n;i++) if(!col[i]){
            cc++;
            if(!dfs(i,1)) return false;
            w[cc]=p[cc][1]-p[cc][2];//printf("w %d %d
    ",cc,w[cc]);
        }
        return true;
    }
    int f[N][N<<1],pa[N][N<<1];
    
    void dp(){
        f[0][0+n]=1;
        for(int i=0;i<cc;i++)
            for(int j=-n;j<=n;j++)
                if(f[i][j+n]) f[i+1][j+n+w[i+1]]=f[i+1][j+n-w[i+1]]=1;
    }
    
    int t1[N],t2[N],c1,c2;
    void print(int s){
        for(int i=cc;i>=1;i--){
            int flag=0;
            if(f[i-1][s+n-w[i]]){flag=1;s-=w[i];}//the color for t1
            else{flag=2;s+=w[i];}
            //printf("s %d
    ",s);
            for(int j=1;j<=p[i][flag];j++) t1[++c1]=tm[i][flag][j];
            flag=3-flag;
            for(int j=1;j<=p[i][flag];j++) t2[++c2]=tm[i][flag][j]; 
        }
        
        printf("%d ",c1);
        for(int i=1;i<=c1;i++) printf("%d ",t1[i]);
        printf("
    %d ",c2);
        for(int i=1;i<=c2;i++) printf("%d ",t2[i]);
    }
    
    int main(int argc, const char * argv[]) {
        n=read();
        for(int i=1;i<=n;i++){
            int v=read();
            while(v!=0) g[i][v]=1,v=read();
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(g[i][j]==0||g[j][i]==0) ins(i,j);
        if(!init()||n==1) printf("No solution");
        else{
            dp();
            for(int i=0;i<=n;i++){
                //printf("hi %d %d %d
    ",cc,n+i,n-i);
                if(f[cc][n+i]) {print(i);break;}
                if(f[cc][n-i]) {print(-i);break;}
            }
        }
        return 0;
    }
    //普通
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    const int N=105;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,g[N][N];
    struct edge{
        int v,ne;
    }e[N*N<<1];
    int h[N],cnt=0;
    void ins(int u,int v){
        cnt++;
        e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
        cnt++;
        e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
    }
    int col[N],cc=0,tm[N][3][N],p[N][3];//team  p
    bool dfs(int u,int c){
        col[u]=c;
        tm[cc][c][++p[cc][c]]=u;
        for(int i=h[u];i;i=e[i].ne){
            int v=e[i].v;
            if(col[u]==col[v]) return false;
            if(!col[v]&&!dfs(v,3-c)) return false;
        }
        return true;
    }
    int w[N];
    bool init(){
        for(int i=1;i<=n;i++) if(!col[i]){
            cc++;
            if(!dfs(i,1)) return false;
            w[cc]=p[cc][1]-p[cc][2];//printf("w %d %d
    ",cc,w[cc]);
        }
        return true;
    }
    int f[N][N<<1],pa[N][N<<1];
    
    void dp2(){
        f[0][0+n]=1;
        for(int i=1;i<=cc;i++)
            for(int j=-n;j<=n;j++){
                if(n+j-w[i]>=0&&f[i-1][n+j-w[i]]){
                    f[i][j+n]=1;
                    pa[i][j+n]=1;//zheng zhe fen pei
                }else if(n+j+w[i]<=2*n&&f[i-1][n+j+w[i]]){
                    f[i][j+n]=1;
                    pa[i][j+n]=-1;
                }
                //printf("f %d %d %d
    ",i,j,f[i][j]);
            }
    }
    
    int t1[N],t2[N],c1,c2;
    
    void print2(int s){
        for(int i=cc;i>=1;i--){
            int flag=0;
            if(pa[i][s+n]==1) {flag=1;s-=w[i];}
            else {flag=2;s+=w[i];}
            
            for(int j=1;j<=p[i][flag];j++) t1[++c1]=tm[i][flag][j];
            flag=3-flag;
            for(int j=1;j<=p[i][flag];j++) t2[++c2]=tm[i][flag][j];    
        }
        printf("%d ",c1);
        for(int i=1;i<=c1;i++) printf("%d ",t1[i]);
        printf("
    %d ",c2);
        for(int i=1;i<=c2;i++) printf("%d ",t2[i]);
    }
    
    
    int main(int argc, const char * argv[]) {
        n=read();
        for(int i=1;i<=n;i++){
            int v=read();
            while(v!=0) g[i][v]=1,v=read();
        }
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(g[i][j]==0||g[j][i]==0) ins(i,j);
        if(!init()||n==1) printf("No solution");
        else{
            dp2();
            
            for(int i=0;i<=n;i++){
                //printf("hi %d %d %d
    ",cc,n+i,n-i);
                if(f[cc][n+i]) {print2(i);break;}
                if(f[cc][n-i]) {print2(-i);break;}
            }
        }
        return 0;
    }
     
     
     
     
  • 相关阅读:
    flutter canvas 简单绘画直线
    Yapi 部署及遇到的坑
    flutter 时间选择器第三方插件返回时间格式说明
    windows + flutter + vscode 连接其他模拟器
    flutter 配置环境
    flutter 返回键监听
    flutter 自定义主题切换
    flutter Provide 状态管理篇
    flutter 本地存储 (shared_preferences)
    JS做倒计时的例题
  • 原文地址:https://www.cnblogs.com/candy99/p/6009289.html
Copyright © 2011-2022 走看看