解题报告 之 SOJ2835 Pick Up Points
Description
Description
Recently loy are very interested in playing a funny game. There is a board of n*m grid. Each grid has one point or not. If two adjacency grids both have one point, someone can pick them up, and then these two grids have no point. The two grids are adjacency only if the Manhattan distence between them is exactly 1. The purpose of the game is to gain as many points as possible.Input
The first line is an interger T, means the number of test cases. Each test case begins with two interger n,m (1 <= n,m <= 100), means the size of the board is n*m. Next n line, each line has m intergers which is 1 or 0, means the grid has one point or not.Output
Output the maximum point loy can get.Sample Input
2 3 4 0 1 0 1 0 1 1 0 1 1 1 1 2 2 1 0 0 1Sample Output
6 0Source
LOY
题目大意:题意非常简单,有一个n*m的棋盘,每个格子有1个或0个棋子,每次操作可以取走相邻的两个棋子(相邻意味着两个格子共享一条边)。问最多一共可以取走多少个棋子。
分析:我感觉挺难的,不过看过题数比其他题还高。。。醉了。这个是CFW模型(牛吃草喝水模型,见另一篇博文POJ
3281 Dining http://blog.csdn.net/maxichu/article/details/45190521 ),但是很难将此问题转化为牛吃草(反正我没想到)。
具体操作如下,将棋盘错开,i+j为奇数的格子为条件一(CFW模型中的F);i+j为偶数的格子为条件二(CFW模型中的D)。然后超级源点向所有条件一的点连一条边,负载为1。所有条件二的点向超级汇点连一条边,负载为1。那么超级汇点的最大流代表最大的配对数。然后要注意遍历所有点,如果它是条件一的点,则连一条边到它周围点(即4个条件二点,如果存在的话)。注意如果是条件二点则不能向周围连一条边,否则流量会错误转移(见图解)。然后跑最大流再乘以2即可。
注意到此时如果5<->2是都双向连接的话那么最大流从2变为3,错误!
上代码:
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<cstdio> #include<sstream> #include<string> #include<cmath> using namespace std; const int MAXN = 20100; const int MAXM = 900000; const int INF = 0x3f3f3f3f; struct Edge { int from, to, cap, next; }; Edge edge[MAXM]; int level[MAXN]; int map[150][150]; int head[MAXN]; int src, des, cnt = 0; void addedge( int from, int to, int cap ) { edge[cnt].from = from; edge[cnt].to = to; edge[cnt].cap = cap; edge[cnt].next = head[from]; head[from] = cnt++; swap( from, to ); edge[cnt].from = from; edge[cnt].to = to; edge[cnt].cap = 0; edge[cnt].next = head[from]; head[from] = cnt++; } int bfs( ) { memset( level, -1, sizeof level ); queue<int> q; while (!q.empty( )) q.pop( ); level[src] = 0; q.push( src ); while (!q.empty( )) { int u = q.front( ); q.pop( ); for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (edge[i].cap&&level[v] == -1) { level[v] = level[u] + 1; q.push( v ); } } } return level[des] != -1; } int dfs( int u, int f ) { if (u == des) return f; int tem; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (edge[i].cap&&level[v] == level[u] + 1) { tem = dfs( v, min( f, edge[i].cap ) ); if (tem > 0) { edge[i].cap -= tem; edge[i ^ 1].cap += tem; return tem; } } } level[u] = -1; return 0; } int Dinic( ) { int ans = 0, tem; while (bfs( )) { while (tem = dfs( src, INF )) { ans += tem; } } return ans; } int main( ) { int kase, m, n; cin >> kase; src = 0; des = 20005; while (kase--) { cin >> n>>m; memset( head, -1, sizeof head ); cnt = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { cin >> map[i][j]; if (map[i][j]) { if ((i + j) % 2 == 1) addedge( src, (i - 1) *100 + j, 1 ); else addedge( (i - 1) * 100 + j, des, 1 ); } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (!map[i][j]) continue; if (i+1<=n&&map[i+1][j]) { addedge( (i - 1) * 100 + j, i * 100 + j, 1 ); } if (j + 1 <= m&&map[i][j+1]) { addedge( (i - 1) * 100 + j, (i - 1) * 100 + j + 1, 1 ); } if (i - 1 >= 1 && map[i-1][j]) { addedge( (i - 1) * 100 + j, (i - 2) * 100 + j , 1 ); } if (j - 1 >= 1 && map[i][j-1]) { addedge( (i - 1) * 100 + j, (i - 1) * 100 + j - 1, 1 ); } } } cout << Dinic() * 2 << endl; } return 0; }
嗯嗯就是这样,看来还是要继续修炼。编程之美好想要Azure的衣服啊!!
时间: 2024-10-03 19:09:02