Codeforces Round #147 (Div. 2) E

题意: 一个目标串和n个其他的串(编号1..n),用其他串求目标串且费用最小。    对其他串的操作:最多能从该串取K个字符,且每取一个的费用是该串的编号,取的字符从其他串删除,加到目标串中。

解法:       最小费用最大流  

建图:      因为,串只含小写字母,所以,目标串化成26个字母,增加超级源点和汇点,26个字母连一条边到汇点,容量为目标    串中该字母的个数,费用为0;对于给出的n个串,统计每一个串中各个字母的个数,并与各个字母连一条边,容量为该串中字母的个数,费用为0;再连向源点一条边到该串,容量和费用都为K。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;

const int maxn = 200 ;
const int maxm = 6000 ;
const int oo = 1<<30 ;
typedef __int64 LL ;
#define clr(p,v) memset(p,v,sizeof(p))

int n,m;
struct Edge { int v,c,len,ne; }e[maxm];
int first[maxn],tot;

void init()
{
	tot = 0;
	clr(first,-1);
}

void add(int x,int y,int z,int c)
{
	e[tot].v=y;e[tot].c=c;e[tot].len=z;e[tot].ne=first[x];first[x]=tot++;
	e[tot].v=x;e[tot].c=0;e[tot].len=-z;e[tot].ne=first[y];first[y]=tot++;
}

int dis[maxn],q[maxn],top,tail,pr[maxm],L,S,T,N;
bool vis[maxn];

bool spfa()
{
	for(int i=1;i<=N;i++) dis[i] = i==S?0:oo;
	clr(vis,true);
	top = 0; tail = 1;
	q[tail] = S;
	while(top != tail)
	{
		top = (top+1)%maxn;
		int cur = q[top];
		vis[cur] = true;
		for(int i=first[cur];i!=-1;i=e[i].ne)
		{
			int vv = e[i].v;
			if(e[i].c && dis[vv] > dis[cur]+e[i].len)
			{
				dis[vv] = dis[cur]+e[i].len;
				pr[vv] = i;//
				if(vis[vv])
				{
					vis[vv] = false;
					tail = (tail+1)%maxn;
					q[tail] = vv;
				}
			}
		}
	}
	return (dis[T]!=oo);
}

int mcmf()
{
	int flow,cost;
	flow = cost = 0;
	while(spfa())
	{
		int u = T,mi = oo;
		while(u != S)
		{
			mi = min(mi,e[pr[u]].c);
			u = e[pr[u]^1].v;
		}
		u = T;
		flow += mi;
		cost += mi*dis[T];
		while(u != S)
		{
			e[pr[u]].c -= mi;
			e[pr[u]^1].c += mi;
			u = e[pr[u]^1].v;
		}
	}
	if(flow != L) return -1;
	return cost;
}

char s[110];
int va[30];

int main()
{
	while(~scanf(" %s%d",s,&n))
	{
		init();
		N = n+28;
		S = N-1; T = N;
		//
		clr(va,0);
		L = strlen(s);
		for(int i=0;i<L;i++) va[s[i]-'a'+1]++;
		for(int i=1;i<=26;i++) add(S,i,0,va[i]);
		//for(int i=1;i<=26;i++) add(i+n,T,0,va[i]);
		//
		for(int i=1;i<=n;i++)
		{
			int k;
			scanf(" %s%d",s,&k);
			clr(va,0);
			for(int j=0;s[j]!=0;j++) va[s[j]-'a'+1]++;
			for(int j=1;j<=26;j++) add(j,i+26,0,va[j]);
			add(i+26,T,i,k);
			//for(int j=1;j<=26;j++) add(i,j+n,0,va[j]);
			//add(S,i,i,k);
		}
		printf("%d\n",mcmf());
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值