hdu 5772 最大权闭合图

链接

http://acm.hdu.edu.cn/showproblem.php?pid=5772

相关知识


http://www.blogbus.com/oibltx-logs/125657936.html
http://wenku.baidu.com/link?url=3tP4ZRiEQIEB63pXFj936pH3Gxk6NMnwks3d4do7WtbRmKjPcEC30YA9V33RH1JqO9pG8oFx2iaRJxP374fy3G6V1VmNpN6-rchMBYvMQj3

  1. 在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。
  2. 最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。

解析


value=w[i][j]+w[j][i]

cost[i]计算, kx为字符x在子串的个数。
cost[x]=0,kx=0

cost[x]=ax(kx1)+bx,kx0

TotalCost=i=09cost[i]

ans=valueTotalcost

ans=valuei=0len(a[i]kxb[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值