poj 2724

来源:互联网 发布:吉林大学校网络教育 编辑:程序博客网 时间:2024/06/03 23:11

这个题比较有意思,我单独开一个来写一写


题意:

题意:迈克有一台可以净化奶酪的机器,用二进制表示净化的奶酪的编号。但是,在某些二进制串中可能包含有‘*'。例如01*100,'*'其实就代表可以取0,1两种情况--> 010100 和011100。现在由于迈克不小心,他以同样的方式弄脏了某些奶酪,问你最少用多少次操作就可以把弄脏的奶酪全净化好。(没有被弄脏过的奶酪不能净化。弄脏过的奶酪可以多次净化。)


思路:

也就是给你一些不同的(判重之后)二进制串,每个串可以通过1次操作净化,也可以把两个只有1位不同的串通过1次操作联合净化.要我们求最少的操作次数.

       我们把所有串按其中1的个数和是奇还是偶分成左右两个点集.

对于任意两个串,如果他们只有1位不同,那么就在他们之间连接一条无向边.(这两个串一定分别属于不同的点集)

       由于串的总数是固定的,且一个串可以通过单独净化也可以通过联合净化.而我们向让净化的次数最少,我们自然想联合净化(即一次可以净化两个串)的次数尽量多了. 那么我们最多可以进行多少次联合净化呢? 这个数值==我们建立二分图的最大匹配边数.(想想是不是,因为一个串最多只能被净化一次)

      假设总的不同串有n个,我们建立二分图的最大匹配数(即联合净化最大次数)为ans,那么我们总共需要n-ans次净化即可.(想想为什么)

       当然本题也可以不用把串特意分成左右点集(本程序实现就是用的这种方式:未分左右点集),我们只需要把原图翻倍,然后求翻倍图的最大匹配数ans,最后用n-ans/2即可.



这个题中有很多位运算的有意思的东西


详见代码

#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<cmath>using namespace std;//顶点编号从0开始的const int MAXN=3000+50;int uN,vN;//u,v数目int g[MAXN][MAXN];int linker[MAXN];bool used[MAXN];int num[MAXN];bool dfs(int u)//从左边开始找增广路径{    int v;    for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改      if(g[u][v]&&!used[v])      {          used[v]=true;          if(linker[v]==-1||dfs(linker[v]))          {//找增广路,反向              linker[v]=u;              return true;          }      }    return false;//这个不要忘了,经常忘记这句}int hungary()//xiongyali{    int res=0;    int u;    memset(linker,-1,sizeof(linker));    for(u=0;u<uN;u++)    {        memset(used,0,sizeof(used));        if(dfs(u)) res++;    }    return res;}//******************************************************************************/int main(){    int n,m;    char str[50];     while(~scanf("%d%d",&n,&m)&&(m+n))     {         memset(g,0,sizeof(g));         memset(num,0,sizeof(num));         int cnt=0;         while(m--){                cnt++;            scanf("%s",&str);            int pos=-1;            for(int i=0;i<n;i++){                if(str[i]=='*'){pos=i;continue;}                num[cnt]|=(str[i]-'0')<<i;            }            if(pos!=-1){                    cnt++;                    num[cnt]=(num[cnt-1]|(1<<pos));            }         }         sort(num+1,num+cnt+1);         num[0]=-1;         int i,j;         for (j=0,i=1;i<=cnt;i++)         {            if (num[j]!=num[i])                num[++j] = num[i];         }         cnt=j;         //cout<<cnt<<endl;         for(i=1;i<=cnt;i++)         for(j=1;j<=cnt;j++){            int c=num[i]^num[j];            if(c&&((c&(c-1))==0))g[num[i]][num[j]]=1;         }         uN=vN=2050;         int ans=cnt-hungary()/2;         printf("%d\n",ans);     }     return 0;}


0 0
原创粉丝点击