我们在研究问题时经常能碰到这一类为题:在某个棋盘上有一种棋子,问最多放下几个棋子。或者有一堆棋子,问你移去最少的棋子数目,使得剩下来的棋子两两不攻击。
先看下面这道题
问题 E: P1035
时间限制: 1 Sec 内存限制: 128 MB
提交: 82 解决: 35
[提交][状态][讨论版]
题目描述
给出一张n*n(n< =100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少1*2的多米诺骨牌进行掩盖。
输入
第一行为n,m(表示有m个删除的格子) 第二行到m+1行为x,y,分别表示删除格子所在的位置 x为第x行 y为第y列
输出
一个数,即最大覆盖格数
样例输入
8 0
样例输出
32
这道题先想的是状压DP(爆搜)后来不会做啦。
其实一个棋子只能和它上下左右4块中的一块拼成一大块,所以我们把每个棋子与其上下左右四个棋子连一条边。连完所有的边后,我们发现要求的就是最多有多少的边(顶点不能重复)想到了什么?二分图匹配。但它不一定是个二分图啊。我们需要证明这一点。一种口糊的方法就是显然一个点仅走奇数次肯定不能回到原点,所以原图中不存在奇数边的环路,就是二分图啦。还有一种比较巧妙和通用的方法就是将棋盘黑白染色,所有黑的只能与白点发生关系,就是显然的二分图了(黑白两个点集)。对于棋子的问题,我们只要在相互冲突的棋子连上一条边,然后简单的证明一下是不是二分图,最后求最大子独立集或最大匹配即可。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define grey 2 #define black 1 #define white 0 int sum,n,m,Map[105][105],color[105][105],num_white,num_black,White[105],Black[10005],used[10005],match[10005]; bool dfs(int u){ int t,i; for (int v=1;v<=num_white;v++) { i=White[v]; if (used[i]==0 && Map[u][i]) { used[i]=1; t=match[i]; match[i]=u; if (t==0 || dfs(t)) return 1; match[i]=t; } } return 0; } void make_way(int u,int v) { Map[u][v]=1; } int make_num(int a,int b) { return ((n)*(a-1)+b); } void make_edge() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(color[i][j]!=grey) { if(color[i][j]==white) { White[++num_white]=make_num(i,j); }else Black[++num_black]=make_num(i,j); if(i>1&&color[i-1][j]!=grey) make_way(make_num(i,j),make_num(i-1,j)); if(j>1&&color[i][j-1]!=grey) make_way(make_num(i,j),make_num(i,j-1)); if(i<n&&color[i+1][j]!=grey) make_way(make_num(i,j),make_num(i+1,j)); if(j<m&&color[i][j+1]!=grey) make_way(make_num(i,j),make_num(i,j+1)); } } void draw(int n) { for(int i=1;i<=n;i++) { if(i%2) color[i][1]=black;else color[i][1]=white; for(int j=2;j<=n;j++) { color[i][j]=1^color[i][j-1]; } } } void print() { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) cout<<color[i][j]; cout<<endl; } } int main() { scanf("%d %d",&n,&m); draw(n); int a,b; for(int i=1;i<=m;i++) scanf("%d %d",&a,&b),color[a][b]=grey; make_edge(); //print(); for(int i=1;i<=num_black;i++) { memset(used,0,sizeof(used)); if(dfs(Black[i]))sum++; //cout<<Black[i]<<endl; } cout<<sum<<endl; }
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define grey 2 #define black 1 #define white 0 int sum,n,m,Map[105][105],color[105][105],num_white,num_black,White[105],Black[10005],used[10005],match[10005]; bool dfs(int u){ int t,i; for (int v=1;v<=num_white;v++) { i=White[v]; if (used[i]==0 && Map[u][i]) { used[i]=1; t=match[i]; match[i]=u; if (t==0 || dfs(t)) return 1; match[i]=t; } } return 0; } void make_way(int u,int v) { Map[u][v]=1; } int make_num(int a,int b) { return ((n)*(a-1)+b); } void make_edge() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(color[i][j]!=grey) { if(color[i][j]==white) { White[++num_white]=make_num(i,j); }else Black[++num_black]=make_num(i,j); if(i>1&&color[i-1][j]!=grey) make_way(make_num(i,j),make_num(i-1,j)); if(j>1&&color[i][j-1]!=grey) make_way(make_num(i,j),make_num(i,j-1)); if(i<n&&color[i+1][j]!=grey) make_way(make_num(i,j),make_num(i+1,j)); if(j<m&&color[i][j+1]!=grey) make_way(make_num(i,j),make_num(i,j+1)); } } void draw(int n) { for(int i=1;i<=n;i++) { if(i%2) color[i][1]=black;else color[i][1]=white; for(int j=2;j<=n;j++) { color[i][j]=1^color[i][j-1]; } } } void print() { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) cout<<color[i][j]; cout<<endl; } } int main() { scanf("%d %d",&n,&m); draw(n); int a,b; for(int i=1;i<=m;i++) scanf("%d %d",&a,&b),color[a][b]=grey; make_edge(); //print(); for(int i=1;i<=num_black;i++) { memset(used,0,sizeof(used)); if(dfs(Black[i]))sum++; //cout<<Black[i]<<endl; } cout<<sum<<endl; }
时间: 2024-10-13 12:33:47