每日三题-Day1-A(POJ 1358 Housing Complexes 预处理+二分匹配)

来源:互联网 发布:dnf装备强化数据 编辑:程序博客网 时间:2024/06/01 09:15

原题地址←_←戳我传送

Housing Complexes
Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 342 Accepted: 118

Description

The Ministry of housing is planning a huge construction project of several housing complexes. Each complex includes several apartments to be sold to government employees at reasonable prices. The ministry has located several big m*n pieces of land that can potentially be used for such construction project; one complex in each land. The lands are all rectangular, each with m*n number of 1*1 square blocks. All housing complexes are h*w rectangles covering exactly h*w blocks of each containing land. 

The problem is that there are originally some old buildings, each covering exactly one block of a land, making it impossible to locate enough free space for all the complexes in order to start the project. Therefore, the ministry has to buy some of these buildings, and demolish them to free the needed space. The old buildings belong to certain number of people. These people are angry of the possibility that their building may be bought and demolished, especially because the government usually pays much less for their buildings compared to the open market prices. 

In response to the protests, the ministry announces a "fair" decision that if it buys some buildings in one land, it will only choose those that belong only to one owner, and will buy all of them at reasonable price. And, it promises not to buy buildings belonging to the same owner in other lands. Note that with this constraint, there may be some lands in which building a complex is impossible. Trying to keep its promises, the ministry has asked you to write a program to see how many housing complexes can be constructed at most with these conditions. 


Input

The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains five integers k (1 <= k <= 30), the number of lands, m and n (1 <= m, n <= 50), the number of rows and columns in each land respectively, and h and w (1 <= h, w <= 50), the number of rows and columns a complex occupies. After the first line, there are k*m lines in the input, representing k lands, each by an m*n matrix. Each line contains a string of length n with no leading or trailing spaces. Each character in the strings represents a block in the land and may be an upper case alphabetic character 'A'..'Z', indicating the owner of the block, or the character '0' indicating the block is free.

Output

There should be one line per test case containing the maximum number of housing complexes that can be constructed for that test case. 

Sample Input

2 3 4 3 3 2 A0B 000 0A0 00B AA0 00B 0B0 000 A0A 000 B00 B00 3 4 3 3 2 A0B 000 0A0 00B AA0 00B 0B0 000 A0A 000 0B0 B00 

Sample Output

32 


题目大意:

有k个岛屿,每个岛屿是块m*n的地皮,每个1*1的地皮可能属于不同的人,用字母A~Z表示,0表示这个1*1地皮不属于任何人

现在想尽可能每个岛屿上都建一个h*w的房子,但是只能在没有人(0表示)的地皮上建。

但是我们可以在一个岛屿上收购一个人的所有地皮,但是在其他岛屿上不能再收购这个人的地皮。收购后的地皮就可以建房子(等同于0地皮)

问最多可以建多少个房子(每个岛屿最多建一个)。


思路:

将岛屿分两种:

1. 如果一个岛屿有一块h*w的0地皮,那么不用再收购,就可以建房子。ans++

2. 如果一个岛屿可以去掉某个字母后能得到一块h*w的地皮,那么这些岛屿可以放到一起权衡最大的方案。


考虑到一个岛屿只能和一个字母对应,求岛屿与字母之间的最大匹配,那就是二分匹配问题。



二分匹配就是个抢老婆的思路:

如果我抢了你老婆,你能找到别的老婆或者抢别人的老婆吗?如果可以,那我就抢你老婆了。匹配数加一。。。。。。


如上图所示的二分图中,匹配步骤如下:

1. 浅紫色的为可行的但是未匹配的边。字母为男,数字为女。遍历所有男节点,为他们找媳妇。


2. A-1 男未婚女未嫁,情投意合了,那就匹配起来吧。黑色为已经匹配的边。A找到媳妇了,那就到下一位。

3.B-1 没有对上眼,所以看B-2。B-2 男未婚女未嫁,也情投意合。再次牵手成功。


4.C可以和1匹配,但是1已经名花有主。询问A是否能成人之美(如果A可以找到另一个媳妇,那么他会成人之美,匹配数加一)


5. A也可以和2匹配,但是同样的,2也名花有主了,再次询问B是否也能将2拱手相让。

6. B去询问3,可以匹配,于是B与3匹配,A和2匹配,C与1匹配,成全三对有情人。


7. 以上言论只为形象化阐述算法的过程,不代表个人观点以及我真的没有大男子主义的!我三观很正啊!


另一种情况,如果B-3不能匹配的话,那么B不会将2拱手相让,A也没有其他对象,A也不会成人之美,所以最后的匹配将会是A-1,B-2苦了C一个单身狗。

但是如果还有4号妹子的话,B在向3妹子表白失败之后,也会向4号妹子表白一次,直至尝试所有的妹子。



AC代码:

#include<cstdio>#include<cstring>using namespace std;char mm[33][55][55];//存地图int num[55][55][33];//num[i][j][k],表示[1~i][1~j]的矩阵里,有多少个字母k。int maps[33][33];//第一位i代表岛屿,第二位j代表字母。如果i岛去掉j字母可以建房,这个值就为1。int use[33];//记录字母是否使用过 0表示没有用过,1为用过int link[33];//记录当前字母是在哪块岛屿中被去掉了(用掉了)int x,y;//x为岛屿的数量,y为字母数量int none[33];//不用去掉字母就可以建房的地;int rob(int a){    for(int i=0; i<y; i++)    {        if(!use[i]&&maps[a][i])        {            use[i]=1;            if(link[i]==-1||rob(link[i]))            {                link[i]=a;                return 1;            }        }    }    return 0;}int MaxMatch(){    int ans = 0;    memset(link,0xff,sizeof(link));    for(int i=0;i<x;i++)    {        //不用去掉字母都能建房子的地,就不要浪费别人的机会        if(none[i]) continue;        memset(use,0,sizeof(use));        if(rob(i))        {            ans++;        }    }    return ans;}int main(){    int t;    int p,n,m,h,w;    scanf("%d",&t);    getchar();    while(t--)    {        scanf("%d%d%d%d%d",&p,&m,&n,&h,&w);        x=p;        y=26;        getchar();        for(int i=0; i<p; i++)        {            for(int j=1; j<=m; j++)            {                for(int k=1; k<=n; k++)                {                    scanf("%c",&mm[i][j][k]);                }                getchar();            }        }        int ans=0;        memset(maps,0,sizeof(maps));        memset(none,0,sizeof(none));        for(int k=0; k<p; k++)        {            memset(num,0,sizeof(num));            for(int i=1; i<=m; i++)            {                for(int j=1; j<=n; j++)                {                    for(int l=0; l<26; l++)                    {                        num[i][j][l]=num[i][j-1][l]+num[i-1][j][l]-num[i-1][j-1][l];//容斥原理                    }                    if(mm[k][i][j]!='0')                        num[i][j][mm[k][i][j]-'A']++;                }            }            int flag2 = 0;            for(int i=h; i<=m; i++)            {                for(int j=w; j<=n; j++)                {                    int flag=0;//有多少种字母                    int ch=0;//那个字母是什么                    for(int l=0; l<26; l++)                    {                        int sum=num[i][j][l]-num[i-h][j][l]-num[i][j-w][l]+num[i-h][j-w][l];                        if(sum!=0)                        {                            flag++;                            ch+=l;//所有字母加起来都没关系,因为flag==1的时候我们才取ch,这时候ch就是唯一的那个字母                        }                    }                    if(flag==0)//只要有一块没有任何字母,我们就能建房子                    {                        ans++;                        flag2=1;                        none[k]=1;                        break;                    }                    else if(flag==1)//这一块只有一种字母,那去掉这种字母就能建房子了                    {                        maps[k][ch]=1;                    }                }                if(flag2) break;            }        }        ans+=MaxMatch();               printf("%d\n",ans);     }    return 0;}


0 0
原创粉丝点击