poj 1636 动态规划

博客介绍了如何使用动态规划解决一个关于监狱囚犯交换的问题,目标是在保证安全的前提下,最大化交换囚犯的数量。题目涉及到将二部图的危险囚犯对分割,并通过整数对确保交换后囚犯不会在同一监狱。作者分享了之前的错误思路——误以为需要应用二分图最大独立集,但实际上应用背包动规更合适。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题意】

有两个监狱,每个监狱里面有n个囚犯,现在希望交换n/2对囚犯。但是考虑有一些原本在不同监狱的囚犯对在一起是很危险的,所以希望经过交换后他们还是不在一个监狱里面。那么如果保证这个条件,希望尽可能多的交换囚犯。

【题解】

和team them up这道题类似。

所有危险囚犯对是可以分割整个二部图为几个集合的,也就是说,比如位于1号监狱的甲如果需要到2号监狱,那么位于2好监狱的所有和甲敌对的囚犯必须得到1号监狱,这样的话可以蔓延这种关系最后形成一个整数对x; y 保证如果甲到2号监狱那么一共要有x个囚犯从1号监狱到2号监狱,而y个囚犯从2号监狱必须得到1号监狱。现在在我们分割二部图为一些整数对后,我们希望选择一部分整数对使它们的和为Sum(x) = Sum(y),并且是小于n=2的最大数。这部分就可以用背包动规来完成。

【教训】

我原先做过team them up这道题,但做这题是却以为用二分图最大独立集来搞,完全飞机了。

【代码】

#include <iostream>
#include <vector>
using namespace std;
const int maxn=405;

vector<int> a[maxn];
int p[maxn],q[maxn];
bool f[maxn][maxn],v[maxn];
int n,m,b,w,tot,mm;

void dfs(int x)
{
     int i,y;
     v[x]=true;
     if (x<=n) w++;
     else b++;
     for (i=0;i<a[x].size();i++)
     {
         y=a[x][i];
         if (v[y]) continue;
         dfs(y);
     }
}

int main()
{
    freopen("pin.txt","r",stdin);
    freopen("pou.txt","w",stdout);
    int i,j,k,cc;
    scanf("%d",&cc);
    while (cc--)
    {
          scanf("%d%d",&n,&m);
          for (i=0;i<=2*n;i++)
              a[i].clear();
          memset(f,0,sizeof(f));
          memset(v,0,sizeof(v));
          tot=0;
          for (i=1;i<=m;i++)
          {
              int x,y;
              scanf("%d%d",&x,&y);
              a[x].push_back(y+n);
              a[y+n].push_back(x);
          }
          for (i=1;i<=n+n;i++)
          if (!v[i])
          {
             b=w=0;
             dfs(i);
             p[++tot]=w;q[tot]=b;
          }
          f[0][0]=true;
          mm=0;
          for (i=1;i<=tot;i++)
          {
              for (j=n/2;j>=p[i];j--)
                  for (k=n/2;k>=q[i];k--)
                  {
                      if (f[j-p[i]][k-q[i]])
                         f[j][k]=true;
                  }
              mm=max(mm+p[i],mm+q[i]);
          }
          for (i=n/2;i>=0;i--)
              if (f[i][i]) break;
          cout << i << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值