描述
一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位置(r,c)
,你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。
分析
- 和星际竞速那道题有相通之处. 当时做星际竞速时总结的建模方法在这里用到了.
- 凡是遇到使每个点都经过一次的题目就可以考虑拆点建图. 拆点, S向Xi连一条容量为1费用为0的边, 表示从X出发. Yi向T连一条容量为1费用为0的边, 表示到达Y.
- 格子X的Xi向原方向指向的格子Y的Yj连一条容量为INF, 费用为0的边, 向另外三个方向指向的格子连一条容量为INF, 费用为1的边. 表示修改.
代码
#include
#include
#include
#include
#include
using namespace std;
const int INF = 1000000000;
const int maxn = 2*15*15 + 10;
struct Edge {
int from, to, cap, flow, cost;
};
struct MCMF {
int n, m, s, t;
vector edges;
vector G[maxn];
int inq[maxn], d[maxn], p[maxn], a[maxn];
void init(int n, int s, int t) {
this->n = n;
this->s = s;
this->t = t;
}
void AddEdge(int from, int to, int cap, int cost) {
edges.push_back((Edge){from, to, cap, 0, cost});
edges.push_back((Edge){to, from, 0, 0, -cost});
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int& cost)
{
for(int i = 0; i < n; i++) d[i] = INF;
memset(inq, 0, sizeof(inq));
d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
queue Q;
Q.push(s);
while(!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
Edge& e = edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
d[e.to] = d[u] + e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap - e.flow);
if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
}
}
}
if(d[t] == INF) return false;
cost += d[t] * a[t];
int u = t;
while(u != s) {
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
u = edges[p[u]].from;
}
return true;
}
int Mincost() {
int cost = 0;
while(BellmanFord(cost));
return cost;
}
}g;
int id[maxn][maxn];
int main()
{
int n, m, s, t;
scanf("%d %d", &n, &m);
int c = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
id[i][j] = ++c;
g.init(c+c+2, s=0, t=c+c+1);
for(int i = 0; i < n; i++) {
char str[20];
scanf("%s", str);
for(int j = 0; j < m; j++) {
g.AddEdge(s, id[i][j], 1, 0);
g.AddEdge(id[i][j]+c, t, 1, 0);
switch(str[j]) {
case 'U': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 0);
g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1);
break;
case 'D': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 0);
g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1);
break;
case 'L': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 0);
g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1);
break;
case 'R': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1);
g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 0);
break;
}
}
}
printf("%d\n", g.Mincost());
return 0;
}