比较裸的费用流吧。(刚好把板子打了一遍)
源点向每个字符连边,流量为要构造的字符串的字符个数,费用为0。
每个字符向每个字符串连边,流量为这个字符串中这个字符出现的次数,费用为i。
每个字符串向汇点连边,流量为每个字符串的限制,费用为0。
然后跑费用流,判断是否满流,如果不是就无解,是就输出花费。
#include<cstdio> #include<cstring> #define S 0 #define T 500 struct edge{ int to,cap,rev,nx,we; }G[40050]; int n,p,ans=0; int h[550],q[40050],d[550]; bool mark[550]; char s[201]; int cnt1[201],cnt[201][27],lim[201]; int Min(int a,int b){return a<b?a:b;} void ae(int s,int e,int c,int w){ G[++p]=(edge){e,c,p+1,h[s],w};h[s]=p; G[++p]=(edge){s,0,p-1,h[e],-w};h[e]=p; } bool spfa(){ memset(d,127/3,sizeof(d)); memset(mark,0,sizeof(mark)); d[T]=0;mark[T]=1; int head=0,tail=0; int inf=d[0]; q[tail++]=T; while(head!=tail){ int fr=q[head++]; for(int i=h[fr];i;i=G[i].nx){ if(G[G[i].rev].cap>0&&d[G[i].to]>d[fr]+G[G[i].rev].we){ d[G[i].to]=d[fr]+G[G[i].rev].we; if(!mark[G[i].to])mark[G[i].to]=1,q[tail++]=G[i].to; } } mark[fr]=0; } return !(d[0]==inf); } int dfs(int s,int t,int f){ mark[s]=1; if(s==t) return f; int sum=0; for(int i=h[s];i;i=G[i].nx){ if(G[i].cap>0&&!mark[G[i].to]&&d[s]-G[i].we==d[G[i].to]){ int d=dfs(G[i].to,t,Min(G[i].cap,f)); if(d)sum+=d,f-=d,G[i].cap-=d,G[G[i].rev].cap+=d; if(f==0)return sum; } } return sum; } int m__f(){ int sum=0; while(spfa()){ mark[T]=1; while(mark[T]){ memset(mark,0,sizeof(mark)); int tmp=dfs(S,T,99999999); sum+=tmp*d[0]; ans+=tmp; } } return sum; } int main(){ scanf("%s",s); int len=strlen(s); for(int i=0;i<len;i++){ cnt1[s[i]-'a'+1]++; } for(int i=1;i<=26;i++){ if(cnt1[i]) ae(S,i,cnt1[i],0); } scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s%d",s,&lim[i]); for(int j=0;j<strlen(s);j++){ cnt[i][s[j]-'a'+1]++; } } for(int i=1;i<=n;i++){ for(int j=1;j<=26;j++){ if(cnt[i][j]) ae(j,i+26,cnt[i][j],i); } ae(i+26,T,lim[i],0); } int cos=m__f(); if(ans!=len) puts("-1"); else printf("%d\n",cos); return 0; }