HDU5772 String problem 最大权闭合图

题目链接:点击打开链接

题目大意:有一个长度为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(kx1)+bx,kx0


解析:

最大权闭合图,首先解释一下什么是闭合图。

对于一个有向图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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值