HDU 3338 网络流 建图

原题位置http://acm.hdu.edu.cn/showproblem.php?pid=3338

这道题的意思就是 给你一个由黑白方格构成的图 白方格全部空着 黑方格有的会有数字

  • 在左下角的数字表示这个方格往下的所有白方格(直到边界或者碰到另外的黑方格) 里面填的数字的和
  • 在右上角的数字表示这个方格往右的所有白方格(直到边界或者碰到另外的黑方格) 里面填的数字的和
  • 问 怎么在白格子里填上1-9的数字 可以让以上两个条件都复合

这题是网络流OTZ 我也是看了题解才做出来的 我的做法是

  • 所有黑方格左下角有数字的 这个作为小源点

  • 所有黑方格右上方有数字的 这个作为小汇点

  • 然后遍历一遍 把黑方格左下角的小源点编上号 同时把他正下方的所有白方格都标记上他的编号 表示这些点的值的和加起来是这个源点流出的 同时超级源点向小源点的容量等于黑方格左下角的数字减去白方格的数量(因为格子里面必须填1-9的数字 但是网络流的下限是0 所以我们把每个格子的容量变成0-8 输出的时候输出9-残量就是这个格子里面填的数字 那么既然每个白格子的容量得少填1 那么小源点的容量就应该减去他管的白格子的个数*1)

  • 然后遍历一遍 把黑方格右上角的小汇点编上号 同时把他正下方的所有黑方格都标记上他的编号 表示这些点的值的和加起来是这个流进这个汇点的 小汇点流进超级汇点的量就等于黑方格右上角数字减去他管的白方格的数量 理由同上

  • 第一个样例的白方格归属的源点如下

    0 0 0 0 0 0
    0 0 1 2 3 0
    0 4 1 2 3 5
    0 4 1 0 3 5
    0 4 1 6 3 0
    0 0 1 6 0 0
    
  • 第一个样例的白方格归属的汇点如下

    0 0 0 0 0 0
    0 0 7 7 7 0
    0 8 8 8 8 8
    0 9 9 0 10 10
    0 11 11 11 11 0
    0 0 12 12 0 0
    
  • 然后遍历一遍 每个白格子就有对应的小源点和小汇点了 小源点和小汇点连边 容量等于8

  • 最后跑一遍dinic 每个白格子对应的边的残量W 9-W就是这个格子上要填的数字 输出 代码如下

  • 关于为什么要用黑格子里的数减去白格子的数目 因为这是有上下界的网络流 流量必须是在1-9 那么处理的方法就是 跑普通的网络流 每个格子的流量是0-8 那么对应的源点提供的或者汇点流入的量就得减少

  • 比如22分配个三个数字 每个数字的大小至少要是1-9 但是网络流跑下来可能有的数字根本就不会有流通过 也就是分配的数字是0 这种情况是不符合条件的 所以要先各分配一个1给他们 那他们剩下的数字大小就是0-8了 用网络流直接跑复合情况 也就是19分给三个0-8的数字 因为每个数字已经有1了OTZ


#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<algorithm>
#include<map>
#define MS(x) memset(x,0,sizeof(x));
#define MS1(x) memset(x,-1,sizeof(x));
using namespace std;
const int maxN = 1e4+5;//点数
const int maxM = 1e6;//边数必须得是给定边数的两倍 因为还有反向边

const int inf = 0x3f3f3f3f;

struct point
{
	bool black;
	int right;
	int down;
	void get()
	{
		char c[8];
		scanf("%s", &c);
		black = (c[3] != '.');
		if (c[0] == 'X') down = -1;
		else
		{
			down = 0;
			for (int i = 0; i < 3; i++)
			{
				down *= 10;
				down += c[i] - '0';
			}
		}
		if (c[4] == 'X') right = -1;
		else
		{
			right = 0;
			for (int i = 4; i < 7; i++)
			{
				right *= 10;
				right += c[i] - '0';
			}
		}
	}
}p[102][102];

class Graph
{
	//先Graph G 生成一个类
	//然后G.init(n) n是点的个数
	//然后addedge加边
	//记得设置源点汇点 set_st(start,end);
	//最后直接跑Dinic() 返回的就是最大流
private:
	int s, t;//源点和汇点
	int n;//点的个数
	int cnt;//前向星
	int Head[maxN];//每个点最后一条边的编号
	int Next[maxM];//指向对应点的前一条边
	int W[maxM];//每一条边的残量
	int V[maxM];//每一条边指向的点
	int Depth[maxN];//分层图的深度
	int cur[maxN];//cur就是记录当前点u循环到了哪一条边
public:
	void init(int n)//源点 汇点 点的个数
	{
		cnt = -1;
		memset(Head, -1, sizeof(Head));
		memset(Next, -1, sizeof(Next));
		this->n = n;
	}
	void set_st(int source, int terminal)//设置源点 汇点
	{
		s = source;
		t = terminal;
	}
	void _Add(int u, int v, int w)//前向星
	{
		cnt++;
		Next[cnt] = Head[u];
		Head[u] = cnt;
		V[cnt] = v;
		W[cnt] = w;
	}
	void Add_Edge(int u, int v, int w)//前向星
	{
		_Add(u, v, w);
		_Add(v, u, 0);
	}
	int dfs(int u, int flow)//u是当前节点,flow是当前流量
	{
		if (u == t)//达到汇点 直接返回
			return flow;
		for (int& i = cur[u]; i != -1; i = Next[i])//注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到记录当前弧的目的
		{
			if ((Depth[V[i]] == Depth[u] + 1) && (W[i] != 0))//如果满足分层图条件并且参量不为0
			{
				int di = dfs(V[i], min(flow, W[i]));//向下增广
				if (di > 0)//增广成功
				{
					W[i] -= di;//正向边减
					W[i ^ 1] += di;//反向边加
					return di;//返回流量
				}
			}
		}
		return 0;//增广失败 返回0
	}
	bool bfs()
	{
		queue<int> Q;
		while (!Q.empty()) Q.pop();
		MS(Depth);
		Depth[s] = 1;//源点的深度为1
		Q.push(s);
		while (!Q.empty())
		{
			int u = Q.front();
			Q.pop();
			for (int i = Head[u]; i != -1; i = Next[i])
				if ((Depth[V[i]] == 0) && (W[i] > 0))//深度等于0并且残量大于0
				{
					Depth[V[i]] = Depth[u] + 1;//它就是下一层
					Q.push(V[i]);
				}
		}
		if (!Depth[t]) return 0;//如果汇点的深度为0 也就是不存在增广路
		return 1;//汇点深度不为0 存在增广路
	}
	int Dinic(int N,int x,int y)
	{
		int Ans = 0;//纪录最大流量
		while (bfs())//存在增广路
		{
			for (int i = 1; i <= n; i++)//当前弧优化
				cur[i] = Head[i];
			while (int d = dfs(s, inf))//源点汇入inf的流量 当还能增广时 一直增加流量
			{
				Ans += d;
			}
		}
		int CNT = 2 * N;
		for (int i = 1; i <= x; i++)
		{
			for (int j = 1; j <= y; j++)
			{
				if (j > 1) printf(" ");
				if (p[i][j].black) printf("_");
				else
				{
					printf("%d", 9-W[CNT]);
					CNT += 2;
				}
			}
			printf("\n");
		}
		return Ans;//返回流量
	}
}G;

int n, m;


struct Source
{
	int column;
	int index;
};

int c[105][105];//每个点对应的源点
int r[105][105];//每个点对应的汇点

int main()
{
	while (cin >> n >> m)
	{
		int s = n * m + 1;
		int t = s + 1;
		G.init(t);
		G.set_st(s, t);
		//输入
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				p[i][j].get();
			}
		}
		//
		int index = 0;
		for (int i = 1; i <= n; i++)
		{ 
			for (int j = 1; j <= m; j++)
			{
				if (!p[i][j].black) continue;
				if (p[i][j].down != -1)
				{
					int len = 0;
					index++;
					for (int k = i + 1; k <= n && !p[k][j].black; k++)
					{
						c[k][j] = index;
						len++;
					}
					G.Add_Edge(s, index, p[i][j].down - len);
				}
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				if (!p[i][j].black) continue;
				if (p[i][j].right != -1)
				{
					int len = 0;
					index++;
					for (int k = j + 1; k <= m && !p[i][k].black; k++)
					{
						r[i][k] = index;
						len++;
					}
					G.Add_Edge(index, t, p[i][j].right - len);
				}
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				if (p[i][j].black) continue;
				G.Add_Edge(c[i][j], r[i][j], 8);
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= m; j++)
			{
				cout << c[i][j] << " ";
			}
			cout << endl;
		}
		G.Dinic(index, n, m);
	}
}


发布了9 篇原创文章 · 获赞 1 · 访问量 586

猜你喜欢

转载自blog.csdn.net/weixin_43149897/article/details/83042449