uva 1627 Team them up! 团队分组

题目:点我


感觉自己好像做麻烦了,这个题目的思考方式是,对于不能在一组的连线,这样所有点就被分为一些连通块。

假如可以分组,每一个连通块就是一个泛化物品,价值可正可负。


这是因为,能在一组的点,可以在一组,也可以不在一组,不能在一组的点一定不能在一组,这样保证了不能的情况,简化了问题。




/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source£ºuva 1627
 *   @type:  dp
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 100   ;
int n;
bool know[maxn+5][maxn+5];
vector<int >G[maxn+5],ve1,ve2;
int color[maxn+5],pre[maxn+5],nex[maxn+5];
int val[maxn+5];
vector<int> has[maxn+5];
bool ok;
bool dp[maxn+5][2*maxn+8];

int fun(int x)
{
    return x+100;
}
int find(int x)
{
    return x==pre[x]?x:pre[x]=find(pre[x]);
}

void merge(int x,int y)
{
    int rootx=find(x);
    int rooty=find(y);
    if(rootx==rooty)  return;
    pre[rootx]=rooty;
}


void getUnknow()
{
    for(int i=1;i<=n;i++)  pre[i]=i;
    for(int i=1;i<=n;i++) G[i].clear(),has[i].clear();

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++) if(i!=j)
        {
            if(!know[i][j]||!know[j][i]  )
            {
                G[i].push_back(j);
                merge(i,j);
            }
        }
    }
}


void dfs(int root,int x,int c)
{
    color[x]=c;
    val[root]+=c;
    has[root].push_back(x);
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(color[y]==color[x])  ok=0;
        if(color[y])  continue;
        int tc= c==1?-1:1;
        dfs(root,y,tc);
    }
}

void add(int x ,int k)
{
    for(int i=0;i<has[x].size();i++)
    {
        int y=has[x][i];
        if(color[y]==1&&k==1||color[y]==-1&&k==-1)  ve1.push_back(y);
        else  ve2.push_back(y);
    }

}
void findsolution(int step,int ans)
{
       if(!step)  return;
       int y=nex[step];

       if(dp[y][fun(ans+val[step] )  ])
        {
                add(   step,-1);
                findsolution(y,ans+val[step]);

                return;
        }

       if(dp[y][fun(ans-val[step] )  ])
        {
                add(   step,+1);
                findsolution(y,ans-val[step]);

                return;
        }


}
bool work()
{
    memset(color,0,(n+1)* sizeof color[0]);
    ok=1;
    for(int i=1;i<=n;i++)  if(pre[i]==i)
    {
        val[i]=0;
        dfs(i,i,1);
    }

    if(!ok)  return false;

    memset(dp,0,sizeof dp);
    dp[0][fun(0)]=1;
    int last=0;
    for(int i=1;i<=n;i++)  if(pre[i]==i)
    {
        nex[i]=last;
        for(int v=-n;v<=n;v++)
        {
            if(  -n<=v-val[i]&&v-val[i]<=n)
           dp[i][fun(v)]|=dp[last][fun(v-val[i])];

           if(-n<=v+val[i]&&v+val[i]<=n)
            dp[i][fun(v)]|=dp[last][fun(v+val[i]) ];
        }

        last=i;
    }


    int ans;
     for(int v=0;v<=n;v++)  if(dp[last][fun(v)])
    {
        ans=v;
        break;
    }

//    printf("%d\n",ans);
    ve1.clear();
    ve2.clear();
    findsolution(last,ans);

     printf("%d",ve1.size());
    for(int i=0;i<ve1.size();i++)
    {
        printf(" %d",ve1[i]);
    }
    putchar('\n');

    printf("%d",ve2.size());
    for(int i=0;i<ve2.size();i++)
    {
        printf(" %d",ve2[i]);
    }
   putchar('\n');

    return true;
}

int main()
{
    int T,kase=0,x;scanf("%d",&T);
    while(T--)
    {
      if(kase++) putchar('\n');
      scanf("%d",&n);
      memset(know,0,sizeof know);
      for(int i=1;i<=n;i++)
      {
          while(~scanf("%d",&x)&&x)
          {
              know[i][x]=1;
          }
      }
      getUnknow();
      if(!work() )   {puts("No solution");continue;}



    }

   return 0;
}


注:完全可以不用并查集

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值