poj 1112

二着色+DP

在不是互相认识的节点之间连一条边,最后得到由多个连通分量构成的图。把图中节点分为两组,保证图中每条边的两个顶点不能再同一组。对每个连通分量进行二着色,每个连通分量的点就分为了a,b两组。假定最后整个图分为1,2两组。每个连通分量的a,b两组,无非一个在1组一个在2组,通过DP,确定每个连通分量中a,b哪个在1组,哪个在2组时最有。然后就是DP的过程,没想出来,看了其他人的结题报告。f[i][j]表示对于前i个联通分量,是否可达到第1组和第2组的差值为j的状态。设a为连通分量i的两组的差值则f[i][j]=1当f[i-1][j-a]==1||f[i-1][j+a]==1。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 using namespace std;
  5 const int maxn=100+10;
  6 int g[maxn][maxn];
  7 int color[maxn];
  8 int mark[maxn][3][maxn];
  9 int amount[maxn][maxn*2];
 10 int co1[maxn][3];
 11 int n,tot;
 12 int f[maxn][maxn*2],path[maxn][maxn*2];
 13 int dfs_color(int u)
 14 {
 15     int i;
 16     for(i=1;i<=n;i++)
 17     {
 18         if(i!=u&&(!g[u][i]||!g[i][u]))
 19         {
 20             if(color[i]==color[u]) return 0;
 21             if(!color[i])
 22             {
 23                 color[i]=3-color[u];
 24                 mark[tot][color[i]][co1[tot][color[i]]++]=i;
 25                 if(!dfs_color(i)) return 0;
 26             }
 27         }
 28     }
 29     return 1;
 30 }
 31 void print(int ans)
 32 {
 33     printf("%d",amount[tot][ans]);
 34     int i,j,loc,tem=ans;
 35     for(i=tot;i>=1;i--)
 36     {
 37         loc=path[i][tem];
 38         for(j=0;j<co1[i][loc];j++) printf(" %d",mark[i][loc][j]);
 39         tem=tem-(co1[i][loc]-co1[i][3-loc]);
 40     }
 41     tem=ans;
 42     printf("\n");
 43     printf("%d",n-amount[tot][ans]);
 44     for(i=tot;i>=1;i--)
 45     {
 46         loc=3-path[i][tem];
 47         for(j=0;j<co1[i][loc];j++) printf(" %d",mark[i][loc][j]);
 48         tem=tem-(co1[i][3-loc]-co1[i][loc]);
 49     }
 50     printf("\n");
 51 }
 52 int main()
 53 {
 54     scanf("%d",&n);
 55     int i,tem,j,a;
 56     for(i=1;i<=n;i++)
 57     {
 58         while(scanf("%d",&tem)&&tem) g[i][tem]=1;
 59     }
 60     int flag=0;
 61     tot=0;
 62     for(i=1;i<=n;i++)
 63     {
 64         if(!color[i])
 65         {
 66             tot++;
 67             color[i]=1;
 68             mark[tot][1][co1[tot][1]++]=i;
 69             if(!dfs_color(i))
 70             {
 71                 flag=1;
 72                 break;
 73             }
 74         }
 75     }
 76     if(flag) printf("No solution\n");
 77     else
 78     {
 79         int a1,a2;
 80         f[1][co1[1][1]-co1[1][2]+n]=1;
 81         f[1][co1[1][2]-co1[1][1]+n]=1;
 82         path[1][co1[1][1]-co1[1][2]+n]=1;
 83         path[1][co1[1][2]-co1[1][1]+n]=2;
 84         amount[1][co1[1][1]-co1[1][2]+n]=co1[1][1];
 85         amount[1][co1[1][2]-co1[1][1]+n]=co1[1][2];
 86         for(i=2;i<=tot;i++)
 87         {
 88             a=co1[i][1]-co1[i][2];
 89             for(j=0;j<=2*n;j++)
 90             {
 91                 if((j-a)>=0&&(j-a)<=2*n&&f[i-1][j-a]==1)
 92                 {
 93                     f[i][j]=1;
 94                     path[i][j]=1;
 95                     amount[i][j]=amount[i-1][j-a]+co1[i][1];
 96                 }
 97                 else if((j+a)>=0&&(j+a)<=2*n&&f[i-1][j+a]==1)
 98                 {
 99                     f[i][j]=1;
100                     path[i][j]=2;
101                     amount[i][j]=amount[i-1][j+a]+co1[i][2];
102                 }
103             }
104         }
105 
106         for(i=n;i<=2*n;i++) if(f[tot][i]) break;
107         for(j=n-1;j>=0;j--) if(f[tot][j]) break;
108         int ans=(i-n)<(n-j)?i:j;
109         print(ans);
110     }
111     return 0;
112 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值