Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) |
【HDU5556 2015合肥赛区E】【最大团or二分图匹配】Land of Farms 不同编号不相邻条件下的最大农场数 最大团做法
#include<stdio.h> #include<iostream> #include<string.h> #include<ctype.h> #include<math.h> #include<map> #include<set> #include<vector> #include<queue> #include<string> #include<algorithm> #include<time.h> #include<bitset> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T> inline void gmax(T &a,T b){if(b>a)a=b;} template <class T> inline void gmin(T &a,T b){if(b<a)a=b;} const int N=12,M=0,Z=1e9+7,ms63=1061109567; const int dy[4]={-1,0,0,1}; const int dx[4]={0,-1,1,0}; int casenum,casei; int n,m,id; char a[N][N]; //原始地图 int o[N][N]; //节点标号 bool e[105][105]; //表示最大团关系,e[i][j]==1表示i与j之间没有排斥关系 int f[105][105]; //把存边方式从邻接矩阵边转化为邻接链表,以此来加快速度 int mx[105]; //mx[i]表示最大团中编号最小的点的编号>=i时的最大团答案 int ans; //全局变量,最后得到的ans就是全局最大团 void color(int y,int x,char ch) { if(y<0||y==n||x<0||x==m||a[y][x]!=ch)return; if(~o[y][x])return;o[y][x]=id; for(int i=0;i<4;++i)color(y+dy[i],x+dx[i],ch); } void disable(int y1,int x1,int y2,int x2) { if(y2<0||y2==n||x2<0||x2==m)return; int o1=o[y1][x1]; int o2=o[y2][x2]; if(o1==o2)return; e[o1][o2]=e[o2][o1]=0; } void COLOR() { id=0;MS(o,-1); for(int i=0;i<n;++i) { for(int j=0;j<m;++j) { if(a[i][j]=='.') { o[i][j]=id++; } else if(o[i][j]==-1) { color(i,j,a[i][j]); id++; } } } } void build_graph() { MS(e,1); for(int i=0;i<n;++i) { for(int j=0;j<m;++j) { for(int k=0;k<4;k++) { disable(i,j,i+dy[k],j+dx[k]); } } } } int mcp(int num,int p) { if(!p)//没有边则直接更新答案 { if(num>ans){ans=num;return 1;} else return 0; } for(int i=0;i<p;++i)//枚举当前节点可以延展的所有节点 { //如果把接下来的所有点都加入最大团中,还无法更新答案的话,返回no if(num+p-i<=ans)return 0; //如果把目前编号最小的节点所能延展的所有点都加进最大团中,还无法更新答案的话,返回no int u=f[num][i];if(mx[u]+num<=ans)return 0; int k=0; for(int j=i+1;j<p;++j)if(e[u][f[num][j]])f[num+1][k++]=f[num][j]; if(mcp(num+1,k))return 1; } return 0; } void MCP() { MS(f,0); MS(mx,0); ans=0; //从后向前,枚举每个点i为最大团中编号最小的点,以此为起点展开搜索 for(int i=id-1;~i;--i) { int k=0; for(int j=i+1;j<id;++j)if(e[i][j])f[1][k++]=j; mcp(1,k); mx[i]=ans; } } int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;++casei) { scanf("%d%d",&n,&m); for(int i=0;i<n;++i)scanf("%s",a[i]); COLOR(); build_graph(); MCP(); printf("Case #%d: %d\n",casei,ans); } } /* 【trick&&吐槽】 题意说不清,题意有毒……这时需要枚举题意,猜解样例Orz 【题意】 给你一个n(10)*m(10)的棋盘。 格子上的'.'代表可以建造新式农场的点,每个新建的农场是独立的,拥有独立的编号。 格子上的'1'~'9'代表可以建造上古农场的点,相同的数字构成的联通块代表相同的上古农场,相同的上古农场共享一个编号。 如果我们要建造一个上古农场,就必须建立其整个联通块。 对于一个编号的农场,其上下左右相邻处不能有其他编号的农场。 基于以上原则,我们希望建造尽可能多数量的农场。 让你输出这个最大数量。 【类型】 最大团or二分图匹配 【分析】 首先看到这道题点数很少,最大供选择农场数才不过100,而且涉及到"我选了你就不能选"这样的限制条件,于是我们考虑到最大团。 于是—— 第一步:求出所有联通块,也就是可能的不同编号的农场数量,并进行编号、染色。 第二步:然后初始化一个全为1的bool矩阵,e[i][j]==1的话表示i和j编号的农场可以同时选。 之后我们对于每个点,向上下左右四个方向走,如果编号不同,那么不同这2个农场不能同时选,在e[][]中标记。 之后对e[][]跑个最大团即可。 本代码里的最大团模板是0base,就是节点编号从0开始,速度很快哦~~啦啦啦! 然而,这道题的正解是二分图匹配,其时间复杂度为O(10^2 * nm * VE)。 O(VE)是匈牙利算法的时间复杂度,最坏情况下是100*200,也就是可以控制使得单组时间复杂度为2e7。 【数据】 1 10 10 .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... */
【HDU5556 2015合肥赛区E】【最大团or二分图匹配】Land of Farms 不同编号不相邻条件下的最大农场数 匈牙利做法
#include<stdio.h> #include<iostream> #include<string.h> #include<ctype.h> #include<math.h> #include<map> #include<set> #include<vector> #include<queue> #include<string> #include<algorithm> #include<time.h> #include<bitset> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T> inline void gmax(T &a,T b){if(b>a)a=b;} template <class T> inline void gmin(T &a,T b){if(b<a)a=b;} const int N=12,M=0,Z=1e9+7,ms63=1061109567; const int dy[4]={-1,0,0,1}; const int dx[4]={0,-1,1,0}; int casenum,casei; int n,m,num,ans; char a[N][N]; bool e[N][N]; //e[i][j]==1表示(i,j)这个'.'是可以通过二分匹配的方式选取的 bool u[10]; //u[i]==1表示'0'+i这个数字是被我们枚举选取的 vector<int>b[105]; //b[i]存放i号点(i号点为偶点)可以匹配的点的编号 int p[105]; //p[j]表示j号点(j号点为奇点)实现匹配的点的编号 bool vis[105]; //vis[i]表示i号点(i号点是偶点)是否匹配访问过 map<int,int>mop; bool color() { for(int i=0;i<n;i++) { for(int j=0;j<m;j++)if(isdigit(a[i][j])&&u[a[i][j]-'0']) { for(int d=0;d<4;d++) { int y=i+dy[d]; int x=j+dx[d]; if(y<0||y==n||x<0||x==m)continue; if(a[y][x]=='.')e[y][x]=0; else if(a[y][x]!=a[i][j]&&u[a[y][x]-'0'])return 0; } } } return 1; } void build_graph() { for(int i=0;i<n*m;i++)b[i].clear(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++)if(a[i][j]=='.'&&e[i][j]&&(i+j)%2==0) { for(int d=0;d<4;d++) { int y=i+dy[d]; int x=j+dx[d]; if(y<0||y==n||x<0||x==m||a[y][x]!='.'||!e[y][x]||(y+x)%2==0)continue; int o1=i*m+j; int o2=y*m+x; b[o1].push_back(o2); } } } } int hungary(int x) { vis[x]=1; for(int i=b[x].size()-1;~i;i--) { int y=b[x][i]; if(p[y]==-1){p[y]=x;return 1;} } for(int i=b[x].size()-1;~i;i--) { int y=b[x][i]; if(!vis[p[y]]&&hungary(p[y])){p[y]=x;return 1;} } return 0; } void HUNGARY() { MS(p,-1); int sum=0; int tmp=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++)if(a[i][j]=='.'&&e[i][j]) { ++sum; MS(vis,0); if(hungary(i*m+j))++tmp; } } gmax(ans,sum-tmp+num); } int main() { scanf("%d",&casenum); for(casei=1;casei<=casenum;++casei) { scanf("%d%d",&n,&m); ans=0; for(int i=0;i<n;++i)scanf("%s",a[i]); mop.clear();int id=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++)if(isdigit(a[i][j])) { if(mop.find(a[i][j])==mop.end())mop[a[i][j]]=id++; a[i][j]=mop[a[i][j]]+'0'; } } int top=1<<id; for(int i=0;i<top;i++) { MS(e,1); MS(u,0); int p=0; int x=i; num=0; while(x) { if(x&1)u[p]=1,++num; x>>=1; ++p; } if(color()) { build_graph(); HUNGARY(); } } printf("Case #%d: %d\n",casei,ans); } } /* 【trick&&吐槽】 题意说不清,题意有毒……这时需要枚举题意,猜解样例Orz 【题意】 给你一个n(10)*m(10)的棋盘。 格子上的'.'代表可以建造新式农场的点,每个新建的农场是独立的,拥有独立的编号。 格子上的'1'~'9'代表可以建造上古农场的点,相同的数字构成的联通块代表相同的上古农场,相同的上古农场共享一个编号。 如果我们要建造一个上古农场,就必须建立其整个联通块。 对于一个编号的农场,其上下左右相邻处不能有其他编号的农场。 基于以上原则,我们希望建造尽可能多数量的农场。 让你输出这个最大数量。 【类型】 最大团or二分图匹配 【分析】 2^10枚举选取的远古农场, 然后把选取的远古农场周围的'.'标记为不可选取。 对于剩余的可以选取的'.',从奇点向相邻的偶点连边, 然后跑二分图最大匹配, 用"可以选取的'.'总数-最大匹配数+枚举选取的远古农场数"更新答案 【数据】 input 1 10 10 .......... .......... .......... .......... .......... .......... .......... .......... .......... .......... output 50 */