CF237E Build String(最小费用最大流)

题目大意:

你需要使用一些字符串s_1s1s_2s2,......,s_nsn来构建字符串t,你可以执行|t|t∣ (|t|t∣是字符串t的长度)次操作:

  1. 从字符串s_1s1s_2s2,......,s_nsn中选择一个非空字符串;
  2. 从所选字符串中选择一个字符并将其写在纸上;
  3. 从所选字符串中删除所选字符。

注意:执行上述操作后,字符串s_1s1s_2s2,......,s_nsn中的字符总数减少1。

我们认为构建出了字符串t,当且仅当写在纸上的字符按顺序连起来为t。

但是还有其他限制:对于每个字符串s_isi,有a_iai为允许从字符串s_isi中删除的最大字符数。

而且,从字符串s_isi中每个删除字符的操作都需要一些代价。对于s_isi,从中删除1个字符需要花费i的代价。

你的任务是计算根据给定规则构建字符串t所需的最小代价。

输入格式

输入的第一行包含字符串t。

第二行包含一个整数n。

接下来的n行,每一行都包含一个字符串s_isi和一个整数a_iai(使用空格隔开),含义如题目所述。

输出格式

输出一个数字,为最小代价。若无解,请输出-1。

题解:

经典最小费用最大流,建图方法在注释里,最小费用最大流还是要更加熟练,在cf那种高压的状态下没有时间调试的。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
const int inf=1e9;
string S[maxn];
string T;
int w[maxn];
int num[26];//目标串中每个数出现的次数 
int c[26][maxn];//每个串中数字出现的次数
int n;
int s,t;
int visit[maxn];
int dis[maxn];//源点到每个点的最小花费(最短路)
int pre[maxn];//前驱
int lst[maxn];//每个点所连的前一条边
int flow[maxn];//源点到此处的流量
int maxflow;//最大流
int mincost;//最小费用
struct node {
    int u,v,flow,dis,nxt;
}edge[maxn*2];
int tot;
int head[maxn];
void addedge (int u,int v,int flow,int dis) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].flow=flow;
    edge[tot].dis=dis;
    edge[tot].nxt=head[u];
    head[u]=tot++;
    
    
    edge[tot].u=v;
    edge[tot].v=u;
    edge[tot].flow=0;
    edge[tot].dis=-dis;
    edge[tot].nxt=head[v];
    head[v]=tot++;
} 
bool spfa (int s,int t) {
    for (int i=0;i<maxn;i++) dis[i]=inf,flow[i]=inf,visit[i]=0;
    queue<int> q;
    q.push(s);
    visit[s]=1;
    dis[s]=0;
    pre[t]=-1;
    while (!q.empty()) {
        int u=q.front();
        q.pop();
        visit[u]=0;
        for (int i=head[u];i!=-1;i=edge[i].nxt) {
            if (edge[i].flow>0&&dis[edge[i].v]>dis[u]+edge[i].dis) {
                dis[edge[i].v]=dis[u]+edge[i].dis;
                pre[edge[i].v]=u;
                lst[edge[i].v]=i;
                flow[edge[i].v]=min(flow[u],edge[i].flow);
                if (!visit[edge[i].v]) {
                    visit[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        }
    }
    return pre[t]!=-1;
} 
void MCMF () {
    while (spfa(s,t)) {
        int u=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        while (u!=s) {
            edge[lst[u]].flow-=flow[t];
            edge[lst[u]^1].flow+=flow[t];
            u=pre[u];
        }
    }
} 

int main () {
    //最小费用最大流
    //0为源点
    //1~26为每个字母代表的点
    //26+1~26+n为每个串代表的点
    
    //27+n为汇点 
    
    //源点向每个字母连一条容量为num[i],费用为0的边
    //每个字母i向每个串j连一条容量为c[i-1][j],费用为j的边
    //每个串i向汇点连一条容量为串的最大删除数,费用为0的边 
    memset(head,-1,sizeof(head));
    cin>>T;
    scanf("%d",&n);
    s=0;
    t=27+n;
    for (int i=1;i<=n;i++) cin>>S[i]>>w[i];
    for (int i=1;i<=n;i++) {
        for (int j=0;j<S[i].length();j++)
            c[S[i][j]-'a'][i]++;
    }
    for (int i=0;i<T.length();i++)
        num[T[i]-'a']++;
    for (int i=1;i<=26;i++)
        addedge(s,i,num[i-1],0);
    for (int i=1;i<=26;i++) {
        for (int j=1;j<=n;j++) {
            addedge(i,26+j,c[i-1][j],j);
        }
    }
    for (int i=1;i<=n;i++)
        addedge(26+i,t,w[i],0);
    MCMF();
    if (maxflow!=T.length()) return printf("-1"),0;
    printf("%d\n",mincost);
}

 

posted @ 2020-08-15 20:31  zlc0405  阅读(169)  评论(0编辑  收藏  举报