【正经题解】方格取数
2024-02-21 17:43:05
发布于:浙江
21阅读
0回复
0点赞
简要介绍一下如何构图
拆点:因为每个方格只取一次,但要走两遍,因此我们考虑对于矩阵中每个方格拆为两个节点,一个为流入点,记为 ;一个为流出 点,记为 '。连两条边从 ’,两条容量都为 ,费用为 [ ][ ]和 。
编号:这个大家有各自的习惯。我的题解中具体看我程序中的 和 函数和注释, 用于编号我前文所提到 的 , 用于编号我前文所提到的 '。
连接节点:每个节点的 连接它的右边和它下边节点的流入点,对于边界特殊处理一下, 连( , )的入点,( , )连 点。
这样构图跑一遍 的最小费用最大流就 了。
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x7f7f7f7f
using namespace std;
struct Edge{
int u;//大多数算法在邻接表中并不需要这个,但费用流比较例外
int v;
int f;//残量
int c;//费用
int next;
}e[850];//网络流的题目都要记得边数开两倍,因为还有反向弧
int head[170];
int n,m,s,t;
int ecnt = 0;
inline void AddEdge(int _u,int _v,int _f,int _c) {
e[ecnt].next = head[_u];
head[_u] = ecnt;
e[ecnt].u = _u;
e[ecnt].v = _v;
e[ecnt].f = _f;
e[ecnt].c = _c;
ecnt++;
}
inline void Add(int _u,int _v,int _f,int _c) {
AddEdge(_u,_v,_f,_c);
AddEdge(_v,_u,0,-_c);
}
int dis[170];
bool inq[170];
int pre[170];
bool SPFA() {
queue <int> q;
q.push(s);
memset(dis,0x7f,sizeof(dis));
memset(inq,0,sizeof(inq));
memset(pre,-1,sizeof(pre));
inq[s] = true;
dis[s] = 0;
while (!q.empty()) {
int cur = q.front();
q.pop();
inq[cur] = false;
for (int i = head[cur];i != -1;i = e[i].next) {
if (e[i].f != 0 && dis[e[i].v] > dis[cur] + e[i].c) {
dis[e[i].v] = dis[cur] + e[i].c;
pre[e[i].v] = i;
if (!inq[e[i].v]) {
inq[e[i].v] = true;
q.push(e[i].v);
}
}
}
}
return dis[t] != INF;
}
void MICMAF(int &flow,int &cost) {
flow = 0;
cost = 0;
while (SPFA()) {
int minF = INF;
for (int i=pre[t];i != -1;i=pre[e[i].u]) minF = min(minF,e[i].f);
flow += minF;
for (int i=pre[t];i != -1;i=pre[e[i].u]) {
e[i].f -= minF;
e[i^1].f += minF;
}
cost += dis[t] * minF;
}
}
/*
节点编号规则:
源点:0
矩阵节点(入):n*x+y+1
矩阵节点(出):n*n+n*x+y+1
汇点:2*n*n+1
*/
int g[10][10];
inline int hashin(int x,int y) {
return n*x+y+1;
}
inline int hashout(int x,int y) {
return n*n + n * x + y + 1;
}
int main() {
memset(head,-1,sizeof(head));
scanf("%d",&n);
int x,y,v;
while (scanf("%d%d%d",&x,&y,&v) == 3) {
if (x == 0 && y == 0 && v == 0) break;
x --;
y --;
g[x][y] = v;
}
s = 0;
t = 2 * n * n + 1;
Add(s,1,2,0);
Add(2*n*n,t,2,0);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++) {
int in = hashin(i,j);
int out = hashout(i,j);
Add(in,out,1,0);//邻接表中后插入的先遍历,卡常,f=1是因为只可能再经过一次
Add(in,out,1,-g[i][j]);
if (i != n - 1) Add(out,hashin(i+1,j),2,0);
if (j != n - 1) Add(out,hashin(i,j+1),2,0);
}
int f,c;
MICMAF(f,c);
printf("%d\n",-c);
return 0;
}
这里空空如也
有帮助,赞一个