对偶问题

考虑一个标准的线性规划问题:

\(max:z=\sum\limits_{i=1}^{n}c_i\cdot x_i\)

限制如下: \[\begin{cases} \sum\limits_{i=1}^{n}a_{1,i}\cdot x_i \leq b_1 \\ \sum\limits_{i=1}^{n}a_{2,i}\cdot x_i \leq b_2 \\ .....\\\sum\limits_{i=1}^{n}a_{m,i}\cdot x_i \leq b_m\end{cases}\]

\(\forall i \in [n] ,x_i \geq0\)

它的对偶形式是:

\(min:z'=\sum\limits_{j=1}^m b_j\cdot y_j\)

限制:

\[\begin{cases} \sum\limits_{j=1}^{m}a_{j,1}\cdot y_j \geq c_1 \\ \sum\limits_{j=1}^{m}a_{j,2}\cdot y_j \geq c_2 \\ .....\\ \sum\limits_{j=1}^{m}a_{j,n}\cdot y_j \geq c_n\end{cases}\]

\(\forall j \in [m],y_j \geq 0\)

用矩阵的语言就是:

prime LP:

\(max:z=\vec{c}^T\cdot \vec{x}\)

\(A\cdot \vec{x} \preceq \vec{b}\)

\(\vec{x} \succeq 0\)

dual LP: \(min:z'=\vec{b}^T\cdot \vec{y}\)

\(A^T\vec{y} \succeq \vec{c}\)

\(\vec{y} \succeq\)

weak duality

上述两个LP,的任意一组feasible solution \(\hat{x},\hat{y}\),有\(\vec{c}^T\cdot \hat{x} \leq \vec{b}^T \cdot \vec{y}\),i.e.最大化一定小于等于最小化.

证:

由于: \(A^T \cdot \hat{y} \succeq \vec{c}\)并且\(\hat{x} \succeq 0\),因而:\(\vec{c}^T\cdot \vec{x} \leq (A^T\cdot \hat{y})^T \hat{x}=\hat{y}^T\cdot A \cdot \hat{x}\)

同样地:因为 \(A\cdot \hat{x} \preceq \vec{b}\),\(\hat{y} \succeq 0\),因而 \(\vec{b}^T\cdot \hat{y} \geq (A \cdot \hat{x})^T \cdot (\hat{y}^T)^T=(\hat{y}^T\cdot A \cdot \hat{x})^T\),注意到 \(\hat{y}^T\cdot A \cdot \hat{x}\)是一个实数,所以 \((\hat{y}^T\cdot A \cdot \hat{x})^T=\hat{y}^T\cdot A \cdot \hat{x}\)

因而:

${}^{T} ^{T} A ^{T} $

互补松弛

假设 \(\hat{x},\hat{y}\)是两个LP的最优解,有\[\vec{c}^T \cdot \hat{x} = \hat{y}^T\cdot A \cdot \hat{x} = \vec{b}^T \cdot \hat{y}\]

我们对其变形: \[\hat{y}^T\cdot A \cdot \hat{x}-\vec{c}^T \cdot \hat{x} = 0\] \[(\hat{y}^T\cdot A-\vec{c}^T)\cdot \vec{x} = 0\]

由于 \(\hat{y}^T\cdot A-\vec{c}^T=(A^T\cdot \hat{y}-\vec{c})^T\),i.e. \((A^T\cdot \hat{y}-\vec{c})^T \cdot \hat{x} =0\)

写成代数形式:\(\sum\limits_{i=1}^n[({\sum\limits_{j=1}^ma_{j,i}\cdot \hat{y}_j})-c_i]\cdot \hat{x}_i = 0\)

这说明了:如果\(x_i\)非0,那么对偶问题中它对应的不等式一定取=,如果一个不等式不取=,那么对偶问题中这个不等式对应的变量一定为0

恶补pytorch系列,数据与项目内容来自:链接,代码是自己写的,和up可能不大一样

这次任务是个18分类的问题, 除了网络结构不一样,其它和上一个基本一致. 只贴代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import numpy as np
import pandas as pd
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torch import nn
from os.path import exists
batch_size=100
word_cnt=29

src=pd.read_csv("./data/data.csv")


class mydata(Dataset):
def __init__(self,typ):
self.data=src[src.part==typ]
self.typ=typ
def __getitem__(self, idx):
sen=[int(x) for x in self.data.iloc[idx]['x'].split(',')]
oht=np.zeros((15,word_cnt))
for i in range(min(len(sen),15)):
oht[i,sen[i]-1]=1
return torch.FloatTensor(oht),int(self.data.iloc[idx]['y'])
def __len__(self):
return len(self.data)
train_set=mydata("train")
train_load=DataLoader(dataset=train_set,batch_size=batch_size,shuffle=True)

test_set=mydata("test")
test_load=DataLoader(dataset=test_set,batch_size=batch_size,shuffle=True)

val_set=mydata("val")
val_load=DataLoader(dataset=val_set,batch_size=batch_size,shuffle=True)

class Mol(nn.Module):
def __init__(self):
super().__init__()
self.h=50
self.mol=nn.Sequential(
nn.Conv1d(15,self.h,5,2),nn.ELU(),
nn.Conv1d(self.h,self.h,5,2),nn.ELU(),
nn.Conv1d(self.h,self.h,5,1),nn.ELU(),
)
self.lin=nn.Linear(self.h,18)
def forward(self,x):
y1=self.mol(x).squeeze(dim=2)
return self.lin(y1)
mynn=Mol()

def test_accuracy(data_load):
with torch.no_grad():
siz=0
ac=0
for data in data_load:
sen,tag=data
out=mynn(sen)
for x,y in zip(out,tag):
x=x.argmax(dim=0)
siz+=1
if x==y:
ac+=1
print("准确率为{:f}".format(ac/siz))
def train_model():
epoch=0
train_step=0
loss_fn=nn.CrossEntropyLoss()
optim=torch.optim.Adam(mynn.parameters(), lr=1e-3)

for epoch in range(30):
print("批次:{}".format(epoch))
for data in train_load:
optim.zero_grad()
sen,tag=data
output=mynn(sen)
res_loss=loss_fn(output,tag)
res_loss.backward()
optim.step()
train_step+=1
if train_step%10==0:
print("训练次数:{},loss:{}".format(train_step,res_loss))
test_accuracy(test_load)
torch.save(mynn.state_dict(),"./model/epoch_{}.pth".format(epoch))
torch.save(mynn.state_dict(),"./model/final.pth")
if not exists("./model/final.pth"):
train_model()
else:
mynn.load_state_dict(torch.load("./model/final.pth"))
test_accuracy(val_load)

输出:准确率为0.707317

恶补pytorch系列,数据与项目内容来自:链接,代码是自己写的,和up可能不大一样

One-hot

将句子分词后,生成一个向量 \(\vec{v}\),向量第 \(i\)维为1当且仅当词\(i\)出现过。 # 本次任务 数据集是csv文件,包括了单词转化为编码的形式,每个句子的标签(0,1二分类),train/val/test标注。 建立一个NN,本次重心不在backbone上,因此骨架只是一个单词数量映射到2的一个线性层。

训练准备

由于每次训练的训练集数据都是批量拿取的,因此我们再复习下Dataset类和Dataloader的使用。

前置条件:

1
2
batch_size=200
word_num=8945

Dataset 类

先调库:

1
from torch.utils.data import Dataset
读入数据:
1
src=pd.read_csv("data/数字化数据.csv")
要继承Dataset类,需要写好构造函数__init__(),重写两个函数__getitem____len__

构造函数可以记录一些基本的数据,记录好表示(比如train/val/test)

1
2
3
def __init__(self,typ):
self.typ=typ
self.data=src[src.part==typ]
这里存下了数据作用与对应的dataframe ### get_item get_item会读入一个参数idx,函数要返回第idx个数据。

这里同时要对数据完成onehot编码,其中-1表示不认识这个词,忽略

1
2
3
4
5
6
7
8
9
10
11
def __getitem__(self, idx):
ts=torch.zeros(word_num,dtype=torch.float)
sentence=self.data.iloc[idx]['x']
seq=[int(x) for x in sentence.split(',')]
for x in seq:
if x!=-1:
ts[x]=1
tag=int(self.data.iloc[idx]['y'])
ts_tar=torch.zeros(2,dtype=torch.float)
ts_tar[tag]=1
return ts,ts_tar
### len 返回数据集大小即可
1
2
def __len__(self):
return len(self.data)

最后产生具体对象

1
train_set=mydata("train")
## Dataloader 调的库在
1
from torch.utils.data import DataLoader
本质是用Dataloader类创建一个对象,几个参数如下: 1. dataset 就是Dataset类构建出来的 2. batch_size 每次取出的个数 3. shuffle最好设为True
1
train_load=DataLoader(dataset=train_set,batch_size=batch_size,shuffle=True)
同样的还要构造好val_load,test_load # 神经网络骨架 不是这次的重点,只整一个线性层:
1
2
3
4
5
6
7
8
9
10
11
from torch import nn
class Mol(nn.Module):
def __init__(self):
super().__init__()
self.mol=nn.Sequential(
nn.Linear(word_num,2),
)
def forward(self, x):
y_out = self.mol(x)
return y_out
mynn=Mol()
模型单个数据最后会输出一个shape为(2)的tensor,分别表示0类和1类的概率。

测试准确率

注意在测试时模型参数不能动,因此要设置

1
with torch.no_grad():
1
2
3
4
5
6
7
8
9
10
11
12
def test_accuracy(data_load):
with torch.no_grad():
siz=0
ac=0
for data in data_load:
sen,tag=data
out=mynn(sen)
for x,y in zip(out,tag):
if (x[0]>x[1])==(y[0]>y[1]):
ac+=1
siz+=1
print("准确率为{}".format(ac/siz))
# 训练过程 ## 工具 损失函数设定为交叉熵,优化器为随机梯度下降,学习速率为0.01
1
2
loss_fn=nn.CrossEntropyLoss()
optim=torch.optim.SGD(mynn.parameters(),lr=0.01)
训练分多个批次(epoch),每个批次都会把所有数据训练一遍,每次训练先算当前神经网络输出,再算损失函数,然后反向传播梯度下降。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for epoch in range(10):
print("训练批次数:{}".format(epoch))
for data in train_load:
optim.zero_grad()
sen,tag=data
output=mynn(sen)
res_loss=loss_fn(output,tag)
res_loss.backward()
optim.step()
if train_step%10==0:
print("训练次数:{},loss={}".format(train_step,res_loss.item()))
train_step+=1
test_accuracy(test_load)
torch.save(mynn.state_dict(),"./model/epoch{}.pth".format(epoch))
## 模型保存/读取 这次我只保存了模型参数:
1
torch.save(mynn.state_dict(),"./model/final.pth")
模型读取(要先创建好骨架):
1
mynn.load_state_dict(torch.load("./model/final.pth"))
## 训练结果 准确率为0.8693622448979592

完整代码

后续放github上

后续大多只简写了证明是NP-H,NP毕竟都比较显然就不写了。。。 # 1.Proof sat is npc ## 先证 sat \(\in\) np-hard:

我们的目标是证明:sat难于任何np问题。考虑任何一个np问题\(L\),存在一个图灵机\(M\),\(x \in L \Leftrightarrow \exist u \in \{0,1\}^{p(x)} s.t.M(x,u)=1\),现在我们的目标是,将\(x\in L\)这个条件多项式地转化为一个CNF。

对于上述TM,我们可以不依赖input纸带地找到它所有的configuration(详见zaq的ppt),对于一个\(x\),我们再设定\(p(x)\)个布尔变量\(y_1,y_2....y_{p(x)}\),要判断\(M(x,\{y\})\)是否会输出1,我们只需要判断以下条件: 1. 存在一个configuration sequence satisfying 每个configuration经过转移函数后都会到达下一个configuration(TM的定义) 2. 最后一个configuration 将会使TM halt and output 1. 这可以在多项式复杂度内表示为两个CNF,因此我们成功将\(x\in L\)转化为了一个CNF。

再证 sat \(\in\) NPC

只要证sat \(\in\) NP,显然的。

2. Proof \(SAT \leq_{p} 3-SAT\)

考虑一个\(k\)元简单析取式\(A_1 \vee A_2 \vee A_3\vee... A_k\)加入一个新的变量\(z\),可以作如下化简

\(A_1 \vee A_2 \vee A_3\vee... A_k = (A_1 \vee A_2 \vee...A_{k-2} \vee z)\wedge (A_{k-1} \vee A_k \vee \bar{z})\),重复上述过程即可,可以发现重复次数显然是线性的,因此是合法的卡普规约.

3. 最大团是 NPC

我们已经证明了3-sat是NPC,因此我们只要证明 \(3-SAT \leq_{p}最大团问题\).

考虑任何一个CNF,我们建如下图:

对于m-clauses CNF,我们建立一个m部图,对于每一个从句,我们将所有成真的赋值拉出来,看成一个顶点,可以发现最多会拉出\(2^3=7\)个点,没有出现的变量用*代替。

然后,对于不矛盾的点,我们连一条边,最后只要判断最大团是否等于\(m\)即可。

4. 整数规划是NPC

考虑证明:\(SAT \leq_{p} 整数规划\)

对于任意mclauses-CNF:\((A_1 \vee A_2...)\wedge (B_1 \vee B_2 \vee...)...\) 先限制所有字母为\(0 \leq x \leq 1\),一共\(m\)个方程,再对每个析取式,写出\(\sum A_i \geq 1\)

显然,整数规划是NP的,因此得证。

5. 有向图哈密顿路是NPC

考虑将问题规约到SAT。我们换一种视角来看待SAT问题,对于每个变量\(u\),如果\(u\)1那么所有包含\(u\)的从句都会被满足,如果\(u\)0那么所有包含\(\bar{u}\)的从句将会被满足。

这次,我们按照变量来建图:

对于一个变量,我们单独对它建一层点:

图片

对于所有的从句\(c_1,c_2,c_3...c_m\),让他们相邻之间用双向边连接,规定从左往右是True,反之是False,如果取True时能让\(c_2\)满足,那么就向点\(c_2\)连边。

现在我们已经处理好了一个变量,要处理所有\(n\)个变量,我们只需要建立起点st和终点ed,按如下方式把所有变量连起来:

图片

每一层的端点处向下一层两个端点都连边。很显然这个图如果存在哈密顿路径那么每一层走的方向就是SAT问题的答案。

6.无向图哈密顿路径是NPC

肯定是证明 \(有向图哈密顿路径 \leq_{p} 无向图哈密顿路径\).

那么给定一个有向图,我们要将它转化成一个无向图,使得哈密顿路存在性相同。对于一个节点,我们作如下转化: 图片 将一个点拆成三个点,可以发现如果要经过中间那个点,一定是要走一个入边和一个出边.

7.哈密顿回路是NPC

考虑将它规约到无向图哈密顿路径

对于原来的无向图,直接加一个点,连接其它所有点即可。

8.TSP判定版本(是否存在小于\(k\)的路径)是NPC

考虑归约到无向图哈密顿路径。

对于一个无向图,给它每条边赋权值\(\frac{k}{n-1}\)即可

9.

证明下述语言是NPC:

\(\{(\phi,1^n): 在ZF公理系统中,命题\phi 有长度至多为n 的证明\}\)

感觉就是规约到SAT?把一个CNF看成一个math statement?

(存疑) # 10.求证二次整数01规划(模2下)是NPC

考虑从3-SAT问题归约

对于任意3-CNF,某个变量会出现\(u\)\(\bar{u}\)的形式。我们构造二次01规划问题,对于CNF中的每个变量,构造二次约束:\(u^2+\bar{u}^2=1\),容易验证这个等式就约束了\(u,\bar{u}\)不同.

这样,我们生成了变量个数个二次约束,接下来,我们要解决每个从句的约束.

考虑到每个从句是一个简单析取式,考虑下列3种情况: 1. 从句只有一个变量\(A\),构造方程\(A^2=1\)即可 2. 从句有两个变量\(A \vee B\),构造方程\(A^2+B^2+AB=1\)即可 3. 从句有三个变量\(A \vee B \vee C\),构造新变量\(t_i\),增加方程

\(\bar{A}\cdot \bar{B}+{t_i}^2=0\)

\(t_i \cdot \bar{C}=0\)

容易验证这些方程的解会是合法的3-SAT解。

11.Exact one 3-sat

从3-sat归约,对于原来的3-sat问题,先把表达式中出现在\(j\)号从句的字母换成\(A_{i,j}\),然后再在末尾增加析取式\((\bar{A_i}\vee A_{i,j} \vee t_i)\) 同样的,还可以证明NAE-3-SAT是NPC # 12. Subsetsum(问题的规模是\(\log值域\)) 从Exact-one 3-sat归约,考虑一个exact-one 3-sat 问题,对于每个变量\(u\),如果它为真,那么将会满足某些从句,如果它为假,则会满足另一些从句,因此,对于每个变量,我们创建两个数,一个数在每个从句2倍二进制位设为1,这样可以阻止进位的发生,在从句个数+2*变量编号处也设为1(保证二择),要求的和就是\(\sum (2i)^2\)

13.点覆盖,独立集

它们和最大团完全是一个东西,略

14.Maxcut is NPC

考虑从NAE-3-SAT归约。对于给定的CNF,每个从句的每个字母都看成一个点,从句之间互相连边,形成许多二元环和三元环,考虑赋值不同的节点之间cut,这样三元环最多贡献是2,二元环是1。

但注意到同一变量在不同的从句之间的约束,因此,我们在一对相反的变量之间添加很多(比m多一个级别?比如\(m^2\))边,相当于惩罚,如果这俩点一样就少cut了这么多条边。最后把边都加上,看看是不是\(2 \cdot 三元环+重边\)

作者应该是俩中国人

Tarski定理

对于完全格 \(<L,\preceq>\),以及\(L \to L\)的单调增函数\(f\)\(f\)一定存在不动点

有点像格上的介值定理 # 文章贡献 对于n阶k维格点格(grid lattice),以及定义在其上的单调不降函数\(f\),构造出了一个\(O(\log^{\lceil \frac{k+1}{2} \rceil}n)\)算法找到一个不动点。

后文假设我们的目标是解决\(Tarski(n,k+1)\) # Step1 Reduce to \(Tarski*(n,k)\) 图片 我们可以单独考虑某一个维度,先固定这个维度的值(也就是说取在这个格中取出一个切片)我们尝试在这个切面上找到一个prefix或者suffix。(前驱或者后继是一定存在的,因为只考虑这个切面(\(k\)维)上,一定存在不动点,这个点肯定是一个前驱或后继),很显然哉suffix到\([n]^{k+1}\)之间(或\([1]^{k+1}\)到prefix之间)一定有不动点,于是第\(k+1\)维减半。

prefix: 指一个点\(x\)满足\(f(x) \preceq x\)

suffix:指一个点\(x\)满足\(x \preceq f(x)\) ## \(Tarski*(n,k)\) 图片

Reduction

图片

问题转变为求解\(Tarski*(n,k)\),假设求解\(Tarski*(n,k)\)的复杂度是\(q(n,k)\),那么,求解\(Tarski(n,k)\)的复杂度是\(O(2^k+k\log n\cdot q(n,k))\)

\(Refined Tarski*(n,k)\)

图片

求解

图片

求解 \(Tarski*(n,a+b)\)

现在的目标是,假设我们能求解\(Tarski*(n,a),Tarski*(n,b)\)如何求解\(Tarski*(n,a+b)\)

一个很自然的想法是,对于\([a+1,a+b]\)维,我们直接枚举它们的值(假设枚举了\(q_i\))然后我们执行如下算法:

图片

此时我们会发现,在前\(a\)维的切面上,\(p^{(l,i)},p^{(r,i)}\)之间的点,这些点的\(g(x)\)\(a+1\)维之后都相等(由于Refined Tarski*的作用),前\(a\)维肯定非负或非正,因此,如果我们能保证\([a+1,a+b+1]\)维也非负/非正,那么就做完了。

如何保证呢,我们发现实际上这可以转化成一个求解\(Tarski(n,b)\)的过程,如果我们可以保证对\(q_i\)的回复满足 Definition2,那么最终就能找到一组非正或非负的回复。

图片

在这里,每次询问\(q_i\)时,我们都会找到之前的询问记录,保证下界要比以前的大,上界要比以前的小,这样的回复一定是可以满足Definition2的(因为\((q_i,0)+g(p^{(l,i)},q_i)\)关于\(q_i\)单调)。因此,假设\(Tarski*(n,a)\)复杂度是\(q(n,a)\),\(Tarski*(n,b)\)复杂度是\(q(n,b)\),那么我们在\(O((b+1)q(n,a)q(n,b))\)内完成了\(Tarski*(n,a+b)\)

最后

由于\(Tarski*(n,2)\)可以在\(O(\log n)\)内完成,因此\(Tarski*(n,k)\)可以在\(O(\log^{k/2}n)\)完成,进而证明了Tarski(n,k)的复杂度

尝试了一个寒假的open problem,先放一段时间再想吧。。

先记录一下目前的思路们: # 1 先用\(d_1\)视角构造一组allocation \(X=(X_1,X_2,X_3)\) 满足对 \(d_1\) tEFX,不失一般性,令 \(X_3 <_{3} X_1 <_{3} X_2\)\(X_1\)中的chores 按照 \(d_3\) 升序排序,即 \(X_1={c_1,c_2....},d_3(c_1)<d_3(c_2)...\) 然后执行

1
2
3
4
5
for i=1 to |X_1|{
if(d_3(X_1-c_i)<d_3(X_3+c_i))break
X_3.add(c_i)
X_1.delete(c_i)
}

此时,必然有 \(d_3(X_3)<d_3(X_1)<d_3(X_2)\),且 \(X_3,X_1\)都对agent3 tEFX-feasible. 同时,\(d_1(X_1)<d_1(X_3)\).

如果 \(X_2\) is tEFX feasible for agent1,那么算法结束

反之,\(X_2\) is not tEFX feasible for agent1, 则一定有\(d_1(X_2)>d_1(X_1)\),此时以\(d_1\)为disutility function使用PR算法,再回到算法开始。 ## 缺陷 无法保证 \(min(d_1(X_1),d_1(X_2))\)单调,无法确定算法是否有穷 已找到一组反例,在\(X_1,X_3\)之间来回传递。

1
2
3
d[1][1]=12;d[1][2]=8;d[1][3]=6;d[1][4]=1;d[1][5]=17;
d[2][1]=7;d[2][2]=1;d[2][3]=11;d[2][4]=2;d[2][5]=1;
d[3][1]=17;d[3][2]=6;d[3][3]=14;d[3][4]=4;d[3][5]=7;

2

先用\(d_1\)视角构造一组allocation \(X=(X_1,X_2,X_3)\) 满足对 \(d_1\) tEFX,不失一般性,令 \(X_3 <_{3} X_1 <_{3} X_2\)\(X_1\)中的chores 按照 \(d_3\) 升序排序,即 \(X_1={c_1,c_2....},d_3(c_1)<d_3(c_2)...\)

如果出现 \(X_1,X_2\)都对agent2,3 不 tEFX feasible, 那么 此时直接对agent2或agent3的disutilities function使用PR算法,在开局就重构

缺陷:

也是没有单调性

3

直接摈弃原来的思路,直接对disutilities function的值域归纳。 也即,假设一组disutilities function可以做出tEFX,只要证明将其中任意一个值增加1仍然可以做出tEFX分配。

正在讨论。。也是一堆问题

upd:讨论不动了,研究值域是没有未来的

题目链接 # 题意

给出一个 \(n\times m\) 的 0/1 矩阵,可以反转若干个位置,再给出 \(A_n,B_m\),要求最终第 \(i\) 行恰有 \(A_i\)\(1\),第 \(j\) 列恰有 \(B_j\)\(1\),问最少需要反转多少个位置,无解输出 -1\(n,m\le 50\)

分析

很自然可以想到网络流来模拟是否对矩阵反转,我的方法如下:

每行,每列都加一个哨兵节点,源点向每个行哨兵节点连边,流量限制为 \(A_i\),行哨兵节点再向矩阵的每个点连边,流量限制为\(1\),矩阵的每个点再向列哨兵节点连边,流量为\(1\),每个列哨兵节点再向汇点连边,流量为\(B_j\)

现在考虑如何衡量操作次数,在行哨兵向矩阵节点连边时,如果 \(a_{i,j}=1\),那么设定费用为 \(-1\),反之设定为\(1\),最后只要判断最大流是否等于 \(\sum A_i\),\(\sum A_i=\sum B_j\),答案为\(cst+\sum a_{i,j}\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define dow(i,j,k) for(i=j;i>=k;--i)
const int N=70;
struct edge{
int nxt,to,w,c;
}e[(N*N)*3];
int head[N*N],pos,s,t,dis[N*N],pre[N*N],ep[N*N],flow,cst;
bool vis[N*N];
void add(int u,int v,int w,int c){
e[++pos]=(edge){head[u],v,w,c};head[u]=pos;
}
bool spfa(){
int i;rep(i,1,t)dis[i]=1<<30;
queue<int>q;
memset(vis,0,sizeof(vis));
rep(i,1,t)pre[i]=-1;
dis[s]=0;vis[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(i=head[u];i;i=e[i].nxt)if(e[i].w>0 && dis[e[i].to]>dis[u]+e[i].c){
dis[e[i].to]=dis[u]+e[i].c;ep[e[i].to]=i;
pre[e[i].to]=u;if(!vis[e[i].to])q.push(e[i].to);vis[e[i].to]=1;
}
}
return dis[t]<(1<<30);
}
void EK(){
int i;
while(spfa()){
int f=1<<30;
for(i=t;i!=s;i=pre[i])
f=min(f,e[ep[i]].w);
flow+=f;cst+=f*dis[t];
for(i=t;i!=s;i=pre[i])e[ep[i]].w-=f,e[ep[i]^1].w+=f;
}
}
int n,m,a[N][N],A[N],B[N];
void eg(int u,int v,int w,int c){
add(u,v,w,c);add(v,u,0,-c);
}
int main(){//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
cin>>n>>m;int i,j;
rep(i,1,n)rep(j,1,m)cin>>a[i][j];
rep(i,1,n)cin>>A[i];rep(i,1,m)cin>>B[i];
int ps=n*m+n+m;pos=1;
s=++ps;t=++ps;
int con=0,slc[2]={1,-1};
rep(i,1,n)rep(j,1,m){
eg(n*m+i,(i-1)*m+j,1,slc[a[i][j]]);
eg((i-1)*m+j,n*m+n+j,1,0);
con+=a[i][j];
}
rep(i,1,n){
eg(s,n*m+i,A[i],0);
}
rep(j,1,m)eg(n*m+n+j,t,B[j],0);
int sa,sb;sa=sb=0;
rep(i,1,n)sa+=A[i];rep(j,1,m)sb+=B[j];
EK();
if(sa==sb && sa==flow)cout<<cst+con;else cout<<-1;
}
0%