『HDU 5727』Necklace(暴力+匈牙利)

转载声明:http://blog.csdn.net/u014325920/article/details/51968827

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5727

题意:

有2n(0<=n<=9)个珠子,分成阴阳两极,每极各n个。

用这2n个珠子做成一个项链,使得相邻两个珠子的极性是不一样的,因为有一些阳性的珠子会被一些阴性的珠子所削弱在它们它们相邻的情况下。

给你m(0<=m<=n*(n-1)/2)个关系[x,y]表示阳性珠子x会被阴性珠子y在相邻情况下所削弱。问你最少有多少个阳性被削弱。

个人感想: 好久没写过二分图题了,看到这题我觉得这道题是二分图,但是我又有另一种感觉,暴力…先说说我暴力思想的来源,因为我认为只要阴珠确定位置了,然后再暴力阳珠插入的位置,不就好了? 例如

阴珠:x 1 x 2 x 3 x 4 x 5 (x代表阳珠可选位置)

因为是环所以只要暴力阳珠的队列就好了…然后再暴力条件,就可以求出答案,我就疑问了,为什么非得二分图?
我觉得肯定有问题,然后我觉得我又卡在贪心的错误位置上了 然后我想到一种情况

假如
5 3
1 2
1 4
1 5
那么不管我阳珠怎么摆都会被阴珠影响到,明显这个贪心是不对了
如果 阴珠:x 1 x 3 x 2 x 4 x 5 这样摆,那么1号就可以摆在第2个位置而没有影响.所以我的暴力如果这样做,
那么起码要 9!*9! …绝对死定了。复杂度太高了 所以只能通过二分图匹配来匹配.

具体做法是 先确定阴珠的位置,然后假设摆阳珠,如果摆在当前位置都没冲突的话,那就建立一个当前阳珠可以选这个位置的边,然后找到最多不冲突的匹配,用N-max得到的就是最小冲突了…具体看看代码,可能讲得不太明白.

/* Author:GavinjouElephant
 * Title:
 * Number:
 * main meanning:
 *
 *
 *
 */

//#define OUT
#include <iostream>
using namespace std;
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <vector>
#include <set>
#include <cstdlib>
#include <map>
#include <queue>
//#include<initializer_list>
//#include <windows.h>
//#include <fstream>
//#include <conio.h>
#define MaxN 0x7fffffff
#define MinN -0x7fffffff
#define Clear(x) memset(x,0,sizeof(x))
const int INF=0x3f3f3f3f;
int N,M;
int x,y;
bool Reflect[15][15];
int matchj[15];
int matchv[15];
bool used[25];

int pos[15];
bool Marix[15][15];
int ans;
void init()
{
    memset(Reflect,false,sizeof(Reflect));
}
bool dfs(int v)
{
    used[v]=true;
    for(int j=1;j<=N;j++)
    {
        if(Marix[v][j])
        {
            int w=matchj[j];
            if(w<0||(!used[w]&&dfs(w)))
            {
                matchj[j]=v;
                matchv[v]=j;
                return true;
            }
        }
    }
    return false;
}
int bipartite_matching()
{
    int res=0;
    memset(matchj,-1,sizeof(matchj));
    memset(matchv,-1,sizeof(matchv));
    for(int v=1;v<=N;v++)
    {
        if(matchv[v]<0)
        {
             memset(used,false,sizeof(used));
             if(dfs(v)){res++;}
        }
    }
    return res;
}
void solve()
{
    ans=INF;
    for(int i=1;i<=N;i++) pos[i]=i;
    do{
        memset(Marix,false,sizeof(Marix));
        int r,l;
        for(int i=1;i<=N;i++)//枚举阳珠的状态
        {
            for(int j=1;j<=N;j++)
            {
                if(j==1)
                {
                    r=pos[j];
                    l=pos[N];
                }
                else
                {
                    r=pos[j];
                    l=pos[j-1];
                }
                if(Reflect[i][l]||Reflect[i][r])continue;//如果阳珠受左右阴珠影响,继续
                Marix[i][j]=true;//可以选当前位置.
            }
        }
        int sum=bipartite_matching();
        ans=min(ans,N-sum);
    }while(next_permutation(pos+2,pos+N+1));//注意先确定一个位置,因为是环,可以这样做,否则+1会超时,因为这时最差情况是 9!*9*9,直接爆炸。。

    printf("%d\n",ans);
}
int main()
{
#ifdef OUT
    freopen("coco.txt","r",stdin);
    freopen("lala.txt","w",stdout);
#endif
    while(scanf("%d%d",&N,&M)!=EOF)
    {
          init();

          for(int i=0;i<M;i++)
          {
              scanf("%d%d",&x,&y);
              Reflect[x][y]=true;
          }
          if(N==0)
          {
             printf("0\n");
             continue;
          }
          solve();
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值