zoukankan      html  css  js  c++  java
  • Codeforces 717G Underfail(最小费用最大流 + AC自动机)

    题目

    Source

    http://codeforces.com/problemset/problem/717/G

    Description

    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.

    Sample Input

    6
    abacba
    2
    aba 6
    ba 3
    3

    Sample Output

    12

    分析

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

    各个模式串在主串匹配的位置可以用AC自动机找到,而这些位置相当于区间。

    其实这题就相当于在一条数轴上选择最大权和的区间,使得各个点被覆盖的区间数不超过x。区间k覆盖问题,POJ3680。。

    我都不会建图了。。要注意的是区间要处理成左闭右开形式,不然比如[1,1]这个区间建图就会出现负环了。

    代码

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define INF (1<<30)
    #define MAXN 555
    #define MAXM 555*1111
    
    struct Edge{
        int u,v,cap,cost,next;
    }edge[MAXM];
    int head[MAXN];
    int NV,NE,vs,vt;
     
    void addEdge(int u,int v,int cap,int cost){
        edge[NE].u=u; edge[NE].v=v; edge[NE].cap=cap; edge[NE].cost=cost;
        edge[NE].next=head[u]; head[u]=NE++;
        edge[NE].u=v; edge[NE].v=u; edge[NE].cap=0; edge[NE].cost=-cost;
        edge[NE].next=head[v]; head[v]=NE++;
    }
    bool vis[MAXN];
    int d[MAXN],pre[MAXN];
    bool SPFA(){
        for(int i=0;i<NV;++i){
            vis[i]=0;
            d[i]=INF;
        }
        vis[vs]=1;
        d[vs]=0;
        queue<int> que;
        que.push(vs);
        while(!que.empty()){
            int u=que.front(); que.pop();
            for(int i=head[u]; i!=-1; i=edge[i].next){
                int v=edge[i].v;
                if(edge[i].cap && d[v]>d[u]+edge[i].cost){
                    d[v]=d[u]+edge[i].cost;
                    pre[v]=i;
                    if(!vis[v]){
                        vis[v]=1;
                        que.push(v);
                    }
                }
            }
            vis[u]=0;
        }
        return d[vt]!=INF;
    }
    int MCMF(){
        int res=0;
        while(SPFA()){
            int flow=INF,cost=0;
            for(int u=vt; u!=vs; u=edge[pre[u]].u){
                flow=min(flow,edge[pre[u]].cap);
            }
            for(int u=vt; u!=vs; u=edge[pre[u]].u){
                edge[pre[u]].cap-=flow;
                edge[pre[u]^1].cap+=flow;
                cost+=flow*edge[pre[u]].cost;
            }
            res+=cost;
        }
        return res;
    }
    
    int tn,ch[55500][26],fail[55500];
    vector<int> vec[55500];
    void insert(char *s,int k){
    	int x=0;
    	for(int i=0; s[i]; ++i){
    		int y=s[i]-'a';
    		if(ch[x][y]==0) ch[x][y]=++tn;
    		x=ch[x][y];
    	}
    	vec[x].push_back(k);
    }
    void getfail(){
    	queue<int> que;
    	for(int i=0; i<26; ++i){
    		if(ch[0][i]) que.push(ch[0][i]);
    	}
    	while(!que.empty()){
    		int x=que.front(); que.pop();
    		for(int i=0; i<26; ++i){
    			if(ch[x][i]){
    				fail[ch[x][i]]=ch[fail[x]][i];
    				que.push(ch[x][i]);
    			}else ch[x][i]=ch[fail[x]][i];
    		}
    	}
    }
    
    int val[111],len[111];
    void ac(char *s){
    	int x=0;
    	for(int i=0; s[i]; ++i){
    		int y=s[i]-'a';
    		x=ch[x][y];
    		for(int tmp=x; tmp; tmp=fail[tmp]){
    			for(int j=0; j<vec[tmp].size(); ++j){
    				int k=vec[tmp][j];
    				addEdge(i-len[k]+1,i+1,1,-val[k]);
    			}
    		}
    	}
    }
    
    char S[555],T[555];
    int main(){
    	int n,m,x;
    	scanf("%d%s%d",&n,S,&m);
    	for(int i=1; i<=m; ++i){
    		scanf("%s%d",T,val+i);
    		len[i]=strlen(T);
    		insert(T,i);
    	}
    	scanf("%d",&x);
    	
    	vs=n+1; vt=vs+1; NV=vt+1; NE=0;
    	memset(head,-1,sizeof(head));
    	addEdge(vs,0,x,0);
    	addEdge(n,vt,x,0);
    	for(int i=1; i<=n; ++i){
    		addEdge(i-1,i,INF,0);
    	}
    	
    	getfail();
    	ac(S);
    	
    	printf("%d",-MCMF());
    	return 0;
    }
    
  • 相关阅读:
    php逻辑操作符中&和&&的异同
    Web性能压力测试工具之WebBench详解
    Entity Framework加载相关实体——延迟加载Lazy Loading、贪婪加载Eager Loading、显示加载Explicit Loading
    EF中的贪婪加载和延迟加载(懒加载)
    WCF中的数据契约(DataContract)
    无废话WCF入门教程六[一个简单的Demo]
    无废话WCF入门教程五[WCF的通信模式]
    无废话WCF入门教程四[WCF的配置文件]
    无废话WCF入门教程三[WCF的宿主]
    无废话WCF入门教程二[WCF应用的通信过程]
  • 原文地址:https://www.cnblogs.com/WABoss/p/5875105.html
Copyright © 2011-2022 走看看