CF237E Build String(最小费用最大流)
题目大意:
你需要使用一些字符串s_1s1,s_2s2,......,s_nsn来构建字符串t,你可以执行|t|∣t∣ (|t|∣t∣是字符串t的长度)次操作:
- 从字符串s_1s1,s_2s2,......,s_nsn中选择一个非空字符串;
- 从所选字符串中选择一个字符并将其写在纸上;
- 从所选字符串中删除所选字符。
注意:执行上述操作后,字符串s_1s1,s_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); }