题目链接:点击打开链接
题目大意:有一个长度为n(n<=100)的串,字符集为'0'-'9'。现在希望它的一个子序列分数最大,分数定义如下
Score=Value - Cost
Value=0;
for(int i=1;i<=length(substr);++i){
for(int j=1;j<=length(substr);++j){
if(i!=j)
Value+=w[id[i]][id[j]];
}
}
cost是子序列中的每个字符出现次数有关的函数
对于字符x有
cost[x]=ax∗(kx−1)+bx,kx≠0
解析:
最大权闭合图,首先解释一下什么是闭合图。
对于一个有向图G,所有点的出边指向的点都在该图内,则该图是一个闭合图。若G的一个子图满足闭合图的定义,则它是G的一个闭合子图。对于一个有点权的有向图,最大权闭合图就是指一个点权之和最大的闭合子图。
最大权闭合图可以解决依赖关系问题。
该题因为权值计算是线性的,所以可以把项进行拆分然后再建图。
将点分为3类
第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分)
第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费)
第三类:对于10种字符拆出10个点,每个点的权值为 -(b[x]-a[x])
最大权闭合图用网络流求解,根据原图建立一个等价的网络,构建规则如下。
对于点权为正的节点,从源点连一条容量为点权的边到该点,对于点权为负的边,从该点连一条容量为点权绝对值的边到汇点。原图中的边保留,容量为inf。最大权值即为图中所有正点权之和减去最大流。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
#include <set>
#include <queue>
#include <map>
#include <bitset>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll INF = 100000000000000000ll;
const int MAXN = 100010;
const int MAXM = 300030;
namespace nwf{
const int N = 10050;
const int M = 100000;
const int INF = 0x3f3f3f3f;
int head[N], cur[N], d[N], st[M], s, e, no;
struct point{
int u, v, flow, next;
point(){};
point(int x, int y, int z, int w):u(x), v(y), next(z), flow(w){};
}p[M];
void add(int x, int y, int z){
p[no] = point(x, y, head[x], z); head[x] = no++;
p[no] = point(y, x, head[y], 0); head[y] = no++;
}
void init(){
memset(head, -1, sizeof(head));
no = 0;
}
bool bfs(){
int i, x, y;
queue<int>q;
memset(d, -1, sizeof(d));
d[s] = 0; q.push(s);
while(!q.empty()){
x = q.front(); q.pop();
for(i = head[x]; i != -1; i = p[i].next){
if(p[i].flow && d[y = p[i].v] < 0){
d[y] = d[x] + 1;
if(y == e) return true;
q.push(y);
}
}
}
return false;
}
int dinic(){
int i, loc, top, x = s, nowflow, maxflow = 0;
while(bfs()){
for(i = s; i <= e; i++) cur[i] = head[i];
top = 0;
while(true){
if(x == e){
nowflow = INF;
for(i = 0; i < top; i ++){
if(nowflow > p[st[i]].flow){
nowflow = p[st[i]].flow;
loc = i;
}
}
for(i = 0; i < top; i++){
p[st[i]].flow -= nowflow;
p[st[i]^1].flow += nowflow;
}
maxflow += nowflow;
top = loc; x = p[st[top]].u;
}
for(i = cur[x]; i != -1; i = p[i].next)
if(p[i].flow && d[p[i].v] == d[x] + 1) break;
cur[x] = i;
if(i != -1){
st[top ++] = i;
x = p[i].v;
}
else {
if(!top) break;
d[x] = -1;
x = p[st[--top]].u;
}
}
}
return maxflow;
}
}
int a[11],b[11],mat[105][105];
char ss[105];
int main()
{
int T,cas=1,n;
cin>>T;
while(T--){
scanf("%d%s",&n,ss);
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",&mat[i][j]);
}
}
int res = 0;
nwf::init();
nwf::s = 0;
nwf::e = 10+n*n+1;
int tot = 1;
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++){
if(i != j){
res += mat[i][j];
nwf::add(nwf::s,tot,mat[i][j]);
nwf::add(tot,n*(n-1)+1+i,inf);
nwf::add(tot,n*(n-1)+1+j,inf);
tot++;
}
}
}
for(int i = 0;i < n;i++){
int id = ss[i]-'0';
nwf::add(tot,nwf::e,a[id]);
nwf::add(tot,n*n+1+id,inf);
tot++;
}
for(int i = 0;i < 10;i++){
if(a[i]>b[i]){
nwf::add(nwf::s,tot++,a[i]-b[i]);
res += a[i]-b[i];
}
else{
nwf::add(tot++,nwf::e,b[i]-a[i]);
}
}
int maxflow;
maxflow = nwf::dinic();
//printf("%d\n",maxflow);
//printf("%d\n",tot);
printf("Case #%d: %d\n",cas++,res-maxflow);
}
return 0;
}