今天花了一天才A了一道题,不过回头上论坛一看发现大家都是做完后感慨良多,感觉也不虚此行。题目要求填充矩阵,且已知矩阵各行各列的和,以及对矩阵某些行某些列有相应的限制,求是否存在合理的填充,存在即输出。
由于各行各列的值已经知道,并且注意到矩阵横行和之和等于就矩阵竖行和之和,我们可以转化出一个流网络,节点是每一行和每一列,假想一个源节点在矩阵左边,汇节点在矩阵上方,则我们要做的就是让流从源经由矩阵留到汇,其中交叉点就是行和列的连边,从而转化为一个含上下界的流网络问题。
当然,由于我手上只有一个Dinic模板(对的,我还是忘了从哪里找来的,作者如果看见了请告诉我,立刻标出来源),而这个模板并不具有处理上下界的功能,所以我们要做进一步处理。对此http://blog.csdn.net/clove_unique/article/details/54884437一文中有很好的讲述,在此只引结论:
建图方法
将有上下界的网络流图转化成普通的网络流图
- 首先建立附加源点ss和附加汇点tt
- 对于原图中的边x->y,若限制为[b,c],那么连边x->y,流量为c-b
- 对于原图中的某一个点i,记d(i)为流入这个点的所有边的下界和减去流出这个点的所有边的下界和
- 若d(i)>0,那么连边ss->i,流量为d(i)
- 若d(i)<0,那么连边i->tt,流量为-d(i)
求解方法
在新图上跑ss到tt的最大流
若新图满流,那么一定存在一种可行流
此时,原图中每一条边的流量应为新图中对应的边的流量+这条边的流量下界
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
#include <queue>
#define MAXN 300
#define MAXE 10000
#define maxm 203
#define maxn 23
#define INF 0x3f3f3f3f
int mValue[maxm];
int nValue[maxn];
int d[maxm+maxn];
int upperValue[maxm][maxn];
int lowerValue[maxm][maxn];
bool isDetermined[maxm][maxn];
int m, n;
void init()
{
memset(upperValue, 0x3f, sizeof(upperValue));
memset(lowerValue, 0, sizeof(lowerValue));
memset(mValue, 0, sizeof(int)*m);
memset(nValue, 0, sizeof(int)*n);
memset(isDetermined, 0, sizeof(isDetermined));
memset(d, 0, sizeof(d));
}
void setBound(int i, int j, int w, char c)
{
//printf("To set %d,%d with %d on %c\n", i, j, w, c);
if (c == '<')
upperValue[i][j] = min(upperValue[i][j], w-1);
else if (c == '>')
lowerValue[i][j] = max(lowerValue[i][j], w+1);
else if (c == '=') {
if (isDetermined[i][j] == false) {
lowerValue[i][j] = w;
upperValue[i][j] = w;
isDetermined[i][j] = true;
}
else if (lowerValue[i][j] != w&&upperValue[i][j] != w)
lowerValue[i][j] = upperValue[i][j] + 1;//manually cause exceptions
}
}
void setBounds(int u, int v, int w,char c)
{
int iStart, iEnd, jStart, jEnd;
iStart = (u == 0) ? 0 : u - 1;
iEnd = (u == 0) ? m - 1 : u - 1;
jStart = (v == 0) ? 0 : v - 1;
jEnd = (v == 0) ? n - 1 : v - 1;
for (int i = iStart; i <= iEnd; i++)
for (int j = jStart; j <= jEnd; j++)
setBound(i, j, w, c);
}
void setBound()
{
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
upperValue[i][j] = min(mValue[i], nValue[j]);
}
}
int constraintNumber, u, v, w;
char compare;
scanf("%d", &constraintNumber);
for (int i = 0; i < constraintNumber; i++) {
scanf("%d %d %c %d", &u, &v, &compare, &w);
setBounds(u, v, w, compare);
}
}
bool simpleCheck()
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (upperValue[i][j] < lowerValue[i][j])
return false;
return true;
}
class matchGraph
{
private:
int s, t;
int cnt;
int Head[MAXN];//the number for the last edge of a certain vertex
int Next[MAXE];//pointing to the former edge of a certain edge?
int V[MAXE];//the vertex each edge points to
int W[MAXE];
int Depth[MAXN];
void _add(int u, int v, int w)
{
Next[cnt] = Head[u];//this mimics a list?
V[cnt] = v;
W[cnt] = w;
Head[u] = cnt;
cnt++;
}
int _sourceNumber;
public:
int order;
void init(int oorder, int ss, int tt)
{//s is the souce and t is the sink
this->order = oorder;
s = ss;
_sourceNumber = 0;
t = tt;
cnt = 0;
memset(Head, -1, sizeof(Head));
memset(Next, -1, sizeof(Next));
}
void addEdge(int u, int v, int w)
{
_add(u, v, w);
_add(v, u, 0);
if (u == s)
_sourceNumber += w;
}
int dfs(int u, int dist)
{//u being the present node and dist being the present flow
if (u == t)
return dist;
int i, di;
for (i = Head[u]; i != -1; i = Next[i]) {
if ((Depth[V[i]] == Depth[u] + 1) && W[i] != 0) {
di = dfs(V[i], min(dist, W[i]));
if (di > 0) {
W[i] -= di;
W[i ^ 1] += di;
return di;
}
}
}
return 0;
}
bool bfs()
{
queue<int > Q;
int u;
while (!Q.empty())
Q.pop();
memset(Depth, 0, sizeof(Depth));
Depth[s] = 1;
Q.push(s);
while (!Q.empty()) {
u = Q.front();
Q.pop();
for (int i = Head[u]; i != -1; i = Next[i]) {
if ((W[i] > 0) && Depth[V[i]] == 0) {
Depth[V[i]] = Depth[u] + 1;
Q.push(V[i]);
}
}
}
return Depth[t] > 0 ? true : false;
}
int Dinic()
{
int Ans = 0, dd;
while (bfs())
while (dd= dfs(s, INF))
Ans += dd;
if (Ans == _sourceNumber)
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
printf("%d%c", W[2 * (i*n + j) + 1] + lowerValue[i][j], j == n - 1 ? '\n' : ' ');
else
printf("IMPOSSIBLE\n");
return Ans;
}
};
int main()
{
matchGraph mG;
int k;
scanf("%d", &k);//k is the number of testcases
for(int l=0;l<k;l++) {
scanf("%d %d", &m, &n);
init();
int size = m + n + 2;//2 for s and t before, 2 for ss and st
mG.init(size, 0, size - 1);
int temp, constraintNumber, row, column, weight;
char c;
for (int i = 0; i < m; i++) {
scanf("%d", &mValue[i]);
d[i] += mValue[i];//for getting the sum of lower bounds
}
for (int i = 0; i < n; i++) {
scanf("%d", &nValue[i]);
d[i + m] -= nValue[i];
}
setBound();
if (!simpleCheck()) {
printf("IMPOSSIBLE\n\n");
continue;
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
mG.addEdge(i + 1, m + 1 + j, upperValue[i][j] - lowerValue[i][j]);
d[i] -= lowerValue[i][j];
d[m + j] += lowerValue[i][j];
}
}
for (int i = 0; i < m+n; i++) {
if (d[i] > 0)
mG.addEdge(0, i + 1, d[i]);
else if (d[i] < 0)
mG.addEdge(i + 1, size - 1, -d[i]);
}
mG.Dinic();
printf("\n");
}
return 0;
}