这个题用到了黑白染色法:
黑白染色法
1 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
1 |
又是一个图,要求是把方格里的所有的1改为零,一次最多只能修改相邻的两个,为最少需要修改几次? 有是一个求最值得问题,但是似乎用于求最值的算法(贪心,动态规划……)都派不上用场,既然在这里提出,那么他肯定能用二分图最大匹配解决,关键是如何建图?
既然是每次只能拿相邻的两个,是两个,正好我们匹配的时候也是找两个进行匹配,这是否就是这个题和最大二分图匹配相联系的地方呢? 对 就是这里。但是每个点能和他四周的四个点匹配,那么我们怎么把所有的点分成来那个部分呢? 对 就是要把第i个点放到第一部分,第I个点周围的四个点放到第二部分,再把这四个点周围的16点放到第1部分
有了这样的思想,我们只需对原图做这样的改动:黑白染色使四周和中间的颜色不同。
白1 |
黑 |
白5 |
黑2 |
白3 |
黑4 |
白 |
黑 |
白6 |
图中黑白的意思是就是把点分类,图里的1,2,3,4,5,6表示的就是上面那个0,1图的1的个数
然后建图,把相邻的点相连,比如说1和2 2和3 。。。。。。。
白色 黑色
1
3 2
5 4
6
然后要把所有一改为零,也就是要对每个点都操作,每个点都要有,那不就是最小顶点覆盖吗?对,这个问题有解决了。
View Code #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<set> #include<cstring> #include<vector> using namespace std; bool G[425][425],visit[425]; int d[4][2] = { 0,1,1,0,0,-1,-1,0 },match[524]; void Get_G( int h , int w ,int map[][15] ) { for( int i = 1; i <= h ; i++ ) { for( int j = 1 ; j <= w; j++ ) { if( map[i][j]!=0 && ( i +j )%2==1 ) { for( int k = 0 ; k < 4 ; k ++ ) { int dx = d[k][0] + i ,dy = d[k][1] + j; if( map[dx][dy]!=0 ) { // printf( "%d %d\n",map[i][j],map[dx][dy] ); G[map[i][j]][map[dx][dy]] = 1; } } } } } } bool path( int num , int N ) { for( int i = 1 ; i <= N ; i++ ) { if( !visit[i] && G[num][i] ) { visit[i] = true; if( match[i]==0 || path( match[i] , N ) ) { match[i] = num; return true; } } } return false; } int main( ) { int N,h,w,map[45][15]; char str[15]; while( scanf( "%d" ,&N )==1 ) { while( N -- ) { int count = 0; memset( G, 0 ,sizeof( G ) ); memset( match , 0 , sizeof( match ) ); memset( map , 0, sizeof( map ) ); scanf( "%d %d",&h,&w ); for( int i = 1 ; i <= h ; i ++ ) { scanf( "%s",str + 1 ); for( int j = 1 ; j <= w ; j ++ ) { if( str[j]=='*' ) map[i][j] = ++count; else map[i][j] = 0; } } Get_G( h , w ,map ); int ans = 0; for( int i = 1 ; i<= count; i ++ ) { memset( visit , 0 , sizeof( visit ) ); if( path( i , count ) ) ans++; } printf( "%d\n",count - ans ); } } //system( "pause" ); return 0; }