codeforces 717 G. Underfail(费用流,好题)

来源:互联网 发布:红五图库永久域名三毛 编辑:程序博客网 时间:2024/05/16 16:20

题目链接
G. Underfail
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You have recently fallen through a hole and, after several hours of unconsciousness, have realized you are in an underground city. On one of your regular, daily walks through the unknown, you have encountered two unusually looking skeletons called Sanz and P’pairus, who decided to accompany you and give you some puzzles for seemingly unknown reasons.

One day, Sanz has created a crossword for you. Not any kind of crossword, but a 1D crossword! You are given m words and a string of length n. You are also given an array p, which designates how much each word is worth — the i-th word is worth pi points. Whenever you find one of the m words in the string, you are given the corresponding number of points. Each position in the crossword can be used at most x times. A certain word can be counted at different places, but you cannot count the same appearance of a word multiple times. If a word is a substring of another word, you can count them both (presuming you haven’t used the positions more than x times).

In order to solve the puzzle, you need to tell Sanz what’s the maximum achievable number of points in the crossword. There is no need to cover all postions, just get the maximal score! Crossword and words contain only lowercase English letters.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 500) — the length of the crossword. The second line contains the crossword string. The third line contains a single integer m (1 ≤ m ≤ 100) — the number of given words, and next m lines contain description of words: each line will have a string representing a non-empty word (its length doesn't exceed the length of the crossword) and integer pi(0 ≤ pi ≤ 100). Last line of the input will contain x (1 ≤ x ≤ 100) — maximum number of times a position in crossword can be used.

Output

Output single integer — maximum number of points you can get.

Example
input
6abacba2aba 6ba 33
output
12
Note

For example, with the string "abacba", words "aba" (6 points) and "ba" (3 points), and x = 3, you can get at most 12 points - the word "aba" appears once ("abacba"), while "ba" appears two times ("abacba"). Note that for x = 1, you could get at most 9 points, since you wouldn’t be able to count both "aba" and the first appearance of "ba".



题意:

说给一个主串和几个有价值的模式串,某个模式串与主串匹配就能累加对应的价值,一个模式串可以在多个位置和主串匹配但同一个位置只能一次,此外主串各个字符最多可以用x次,问如何匹配使获得的价值最大。


题解:

可以暴力得到每个模式串在主串中能够匹配时的起始位置和终点位置(也可以用AC自动机),然后是区间覆盖问题了,和这个题一样。

不过要注意,假如一个覆盖的区间为[l,r],那么加边是应该是[l,r+1),不然比如[1,1]这个区间建图就会出现负环了(求费用时有spfa过程)

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#include<queue>#include<stack>using namespace std;#define rep(i,a,n) for (int i=a;i<n;i++)#define per(i,a,n) for (int i=n-1;i>=a;i--)#define pb push_back#define fi first#define se secondtypedef vector<int> VI;typedef long long ll;typedef pair<int,int> PII;const ll mod=1e9+7;const int maxn=500+10;char s[maxn],s1[110];const int MAXM = maxn*maxn;const int INF = 0x3f3f3f3f;struct Edge{    int to,next,cap,flow,cost;}edge[MAXM];int head[maxn],tol;int pre[maxn],dis[maxn];bool vis[maxn];int N;//节点总个数,节点编号从0~N-1void init(int n){    N = n;    tol = 0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int cap,int cost){    edge[tol].to = v;    edge[tol].cap = cap;    edge[tol].cost = cost;    edge[tol].flow = 0;    edge[tol].next = head[u];    head[u] = tol++;    edge[tol].to = u;    edge[tol].cap = 0;    edge[tol].cost = -cost;    edge[tol].flow = 0;    edge[tol].next = head[v];    head[v] = tol++;}bool spfa(int s,int t){    queue<int> q;    for(int i = 0;i < N;i++)    {        dis[i] = INF;        vis[i] = false;        pre[i] = -1;    }    dis[s] = 0;    vis[s] = true;    q.push(s);    while(!q.empty())    {        int u = q.front();        q.pop();        vis[u] = false;        for(int i = head[u]; i != -1;i = edge[i].next)        {            int v = edge[i].to;            if(edge[i].cap > edge[i].flow &&               dis[v] > dis[u] + edge[i].cost )            {                dis[v] = dis[u] + edge[i].cost;                pre[v] = i;                if(!vis[v])                {                    vis[v] = true;                    q.push(v);                }            }        }    }    if(pre[t] == -1)return false;    else return true;}//返回的是最大流,cost存的是最小费用int minCostMaxflow(int s,int t,int &cost){    int flow = 0;    cost = 0;    while(spfa(s,t))    {        int Min = INF;        for(int i = pre[t];i != -1;i = pre[edge[i^1].to])        {            if(Min > edge[i].cap - edge[i].flow)                Min = edge[i].cap - edge[i].flow;        }        for(int i = pre[t];i != -1;i = pre[edge[i^1].to])        {            edge[i].flow += Min;            edge[i^1].flow -= Min;            cost += edge[i].cost * Min;        }        flow += Min;    }    return flow;}int main(){    int n;    scanf("%d",&n);    scanf("%s",s+1);    int l=strlen(s+1);    init(l+2);    int m;    scanf("%d",&m);    rep(i,1,m+1)    {        scanf("%s",s1+1);        int v;        scanf("%d",&v);        int l1=strlen(s1+1);        for(int i=1;i<=l-l1+1;i++)        {            bool f=true;            for(int j=1;j<=l1;j++)                if(s[i+j-1]!=s1[j])                {                    f=false;                    break;                }            if(f)                addedge(i,i+l1,1,-v);        }    }   // cout << tol << endl;   rep(i,1,l) addedge(i,i+1,INF,0);   //cout << tol << endl;    int x;    scanf("%d",&x);    addedge(0,1,x,0),addedge(l,l+1,INF,0);    int ans=0;    minCostMaxflow(0,l+1,ans);    printf("%d\n",-ans);    return 0;}



0 0