Asa’s Chess Problem
先闡述一下帶上下界的邊怎麼建.
帶上下界的建圖方法
設我要建一條邊,流量上界爲,下界爲,費用爲.則我需要建兩條邊.
爲保證一定會有的流量流過去,我們可以建立一條的邊,容量爲,費用爲,這樣保證了一旦有解,一定會有的流量流過來.
隨後,再建立一條從的邊,容量爲,費用爲的邊.這樣保證了還會再流至多的流量,但不是強制要流.
判斷解是否有解
算出所有邊的之和,算出最後的費用包含個.如果則說明有解,否則無解.
計算實際的費用
實際的費用就是.
上面的式子就是實際的答案.
正文
簡單情形
先考慮我們拿到的矩陣之間的元素是不可交換的,要求檢查該矩陣滿足條件的可行性.
那麼這是一個經典問題,建圖方法如下:
源點向行連接一條流量爲該行上下界且費用爲的邊.
列向匯點連接一條流量爲該列上下界且費用爲的邊.
每個點表示從向連接一條容量爲點的權值,費用爲的邊.最後看一下是否滿流且下界邊流滿即可.
複雜情形
那麼回到這個題,相當於增加了格點之間可交換這個條件(注意可交換的格點對之間至少有一個座標是相同的,這是題目給出的條件之一).
如果兩個格點相同,那麼完全沒有交換的必要(直接按照簡單情形連邊即可),因此我們只考慮兩個格點不同的情況.
設爲黑色(1),爲白色(0).考慮它們之間連邊方案.
- 當時,無論交換與否,最後流量都是流向這列.但是交換與否影響流量是從行流入還是行流入.
- 當時,無論交換與否,最後流量都是從這行流出.但是交換與否影響流量是從行流出還是行流出.
實現代碼
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
using namespace std;
#define int long long
const int inf = 1e10;
const int mm = 111111;
const int maxn = 3000;
int node,src,dest,edge;
int ver[mm],flow[mm],cst[mm],nxt[mm];
int head[maxn],work[maxn],dis[maxn],q[maxn];
int tot_cost;
void prepare(int _node,int _src,int _dest)
{
node=_node,src=_src,dest=_dest;
for(int i=0; i<node; ++i)head[i]=-1;
edge=0;
tot_cost = 0;
}
void add_edge(int u,int v,int c,int cost)
{
ver[edge]=v,flow[edge]=c,nxt[edge]=head[u],cst[edge]=cost,head[u]=edge++;
ver[edge]=u,flow[edge]=0,nxt[edge]=head[v],cst[edge]=-cost,head[v]=edge++;
}
int ins[maxn];
int pre[maxn];
bool Dinic_spfa()
{
memset(ins,0,sizeof(ins));
//memset(dis,inf,sizeof(dis));
rep(i,0,maxn-1) dis[i] = 10000*inf;
memset(pre,-1,sizeof(pre));
queue<int> Q;
//int i,u,v,l,r=0;
Q.push(src);
dis[src] = 0,ins[src] = 1;
pre[src] = -1;
while(!Q.empty()){
int u = Q.front();Q.pop();
ins[u] = 0;
for(int e = head[u];e != -1;e = nxt[e]){
int v = ver[e];
if(!flow[e]) continue;
if(dis[v] > dis[u] + cst[e]){
dis[v] = dis[u] + cst[e];
pre[v] = e;
if(!ins[v]) ins[v] = 1,Q.push(v);
}
}
}
return dis[dest] < 10000*inf;
}
int Dinic_flow()
{
int i,ret=0,delta=inf;
while(Dinic_spfa())
{
for(int i=pre[dest];i != -1;i = pre[ver[i^1]])
delta = min(delta,flow[i]);
for(int i=pre[dest];i != -1;i = pre[ver[i^1]])
flow[i] -= delta,flow[i^1] += delta;
ret+=delta;
tot_cost += dis[dest]*delta;
}
return ret;
}
int n;
int a[55][55];
signed main() {
std::ios::sync_with_stdio(false);
while(std::cin >> n && n) {
int suml = 0,who = 0;
prepare(2+2*n+n*n/2,0,2*n+1);
rep(i,1,n) rep(j,1,n) {
std::cin >> a[i][j];
who += a[i][j];
}
rep(i,1,n) {
int l,h;
std::cin >> l >> h;
suml += l;
if(l > 0)
add_edge(0,i,l,-inf);
if(h-l > 0)
add_edge(0,i,h-l,0);
}
rep(i,1,n) {
int l,h;
std::cin >> l >> h;
suml += l;
if(l > 0)
add_edge(n+i,2*n+1,l,-inf);
if(h-l > 0)
add_edge(n+i,2*n+1,h-l,0);
}
int tot = 2*n+1;
rep(i,1,n*n/2) {
int x1,x2,y1,y2;
std::cin >> x1 >> y1 >> x2 >> y2;
if(a[x1][y1] + a[x2][y2] == 2) {
add_edge(x1,n+y1,1,0);
add_edge(x2,n+y2,1,0);
}
else if(a[x1][y1] + a[x2][y2] == 1) {
++tot;
if(y1 == y2) {
add_edge(x1,tot,1,!a[x1][y1]);
add_edge(x2,tot,1,!a[x2][y2]);
add_edge(tot,n+y1,1,0);
}
else if(x1 == x2){
add_edge(x1,tot,1,0);
add_edge(tot,n+y1,1,!a[x1][y1]);
add_edge(tot,n+y2,1,!a[x2][y2]);
}
}
}
int myflow = Dinic_flow();
tot_cost *= -1;
int pass = (tot_cost+inf-1)/inf;
if(pass != suml || myflow != who) {
std::cout << -1 << std::endl;
continue;
}
std::cout << (inf - (tot_cost % inf))%inf << std::endl;
}
return 0;
}