链接
相关知识
http://www.blogbus.com/oibltx-logs/125657936.html
http://wenku.baidu.com/link?url=3tP4ZRiEQIEB63pXFj936pH3Gxk6NMnwks3d4do7WtbRmKjPcEC30YA9V33RH1JqO9pG8oFx2iaRJxP374fy3G6V1VmNpN6-rchMBYvMQj3
- 在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。
- 最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。
解析
value=w[i][j]+w[j][i]
cost[i]计算, kx为字符x在子串的个数。
cost[x]=0,kx=0
cost[x]=ax∗(kx−1)+bx,kx≠0
TotalCost=∑i=09cost[i]
ans=value−Totalcost
ans=value−∑i=0len(a[i]∗kx−b[i])
如式选择不同的子串的时候,ans将会不同。为了让ans最大,就让Totalcost最小,由此可以构建闭合图,闭合图中将点权赋值。
1. 第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分)
2. 第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费)
3. 第三类:对于10种字符拆出10个点,每个点的权值为 -(b[x]-a[x]);
若选择第一类中的点,就要选择他以后的几个点,这个模型就可以想到最大流最小割问题。
建图:
由闭合图构建时,建立超级源点和超级汇点,闭合图中点权为正,与源点建容量为该点权的边,点权为负,与汇点建立容量为该点权绝对值的边,闭合图内部的边,为了不让进入最小割,容量都为inf。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 100+10;
const int inf = 0x3f3f3f3f;
const int MAXN = 11000;
char s[maxn];
int a[20], b[20];
int c[maxn];
int head[MAXN];
int matrix[maxn][maxn];
int S=0, T = MAXN-10;
struct node{
int t;
int f;
int next;
node() {};
node(int _t, int _f, int _next) {
t = _t;
f = _f;
next = _next;
}
}edge[1000000+10];
int id[maxn];
int cnt;
void init() {
cnt=0;
memset(head, -1, sizeof(head));
}
int N;
void add(int u, int v, int f) {
edge[cnt] = node(v, f, head[u]);
head[u] = cnt++;
edge[cnt] = node(u, 0, head[v]);
head[v] = cnt++;
}
int level[MAXN];
bool bfs() {
memset(level, 0, sizeof(level));
queue<int>q;
q.push(S);
level[S] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
if (u == T)
return true;
for (int i=head[u]; i!=-1; i=edge[i].next) {
int v=edge[i].t;
if (level[v] == 0 && edge[i].f > 0) {
q.push(v);
level[v] = level[u] + 1;
}
}
}
return false;
}
int dfs(int u, int maxf) {
if (u == T)
return maxf;
int ret = 0;
for(int i=head[u]; i!=-1; i=edge[i].next) {
int v = edge[i].t, f = edge[i].f;
if (level[v] == level[u] + 1 && f > 0) {
int rf =dfs(v, min(maxf-ret, f));
edge[i].f -= rf;
edge[i^1].f += rf;
ret += rf;
if (ret == maxf)
return maxf;
}
}
return ret;
}
int Dinic() {
int flow=0;
while (bfs())
flow += dfs(0, inf);
return flow;
}
int main()
{
int C;
scanf("%d", &C);
int ca=1;
while (C--) {
init();
int n;
scanf("%d%s", &n, s);
N=1;
int res = 0;
for (int i=0; i<10; i++)
scanf("%d%d", &a[i], &b[i]);
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) {
scanf("%d", &matrix[i][j]);
res += matrix[i][j];
}
}
for (int i=0; i<n; i++) {
c[i] = n*(n-1)/2+i+1;
int j = s[i]-'0';
add(c[i], T, a[j]);
}
int v = 1;
for (int i=0; i<n; i++) {
for (int j=i+1; j<n; j++) {
add(S, v, matrix[i][j]+matrix[j][i]);
add(v, c[i], inf);
add(v, c[j], inf);
v++;
}
}
for (int i=0; i<10; i++)
id[i] = n*(n-1)/2+n+i+1;
for (int i=0; i<n; i++) {
int u = c[i];
int v = s[i]-'0';
add(u, id[v], inf);
}
for (int i=0; i<10; i++)
add(id[i], T, b[i]-a[i]);
res-=Dinic();
printf("Case #%d: %d\n", ca++, res);
}
return 0;
}