題目鏈接:點擊打開鏈接
題目大意:有一個長度爲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;
}