[Underfail][CodeForces.717.G][费用流]

来源:互联网 发布:美国大数据公司 年薪 编辑:程序博客网 时间:2024/06/06 02:33

题目

Bubble Cup 9 - Finals [Online Mirror]

题意

你进入了一个地下城, 有两只骷髅有事没事就给你出题, 说好的邂逅呢?

这一天, 有一只骷髅给了你一个长为n的字符串, 和m个用来匹配的字符串, 每个字符串有个权重p分, 每个位置的字符都只能用x次, 问你最多能拿几分

用例解析就是:

分析

看上去是KMP, 其实是一道费用流, 建边如图

相邻两个字符之间建一条费用为0, 流量为x的边, 表示从源点到汇点最多可以流过x的流量.

在可匹配的子串的头和尾+1建立一条费用为-p, 流量为1的边.

最后用最小费用流跑一遍就OK了.

AC代码


第一次TLE, 建边的时候只在匹配的子串的头和尾建边, 遇到用例是单字符的情况, 建了自环QAQ.

第二次WA, 建点只建到上图的END, 总流量计算有误orz, 多添加一个节点(汇点)后AC.

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<cstdlib>#include<queue>#include<vector>#include<stack>#include<string>using namespace std;#define lson l,m,x<<1#define rson m+1,r,x<<1|1const int INF = 0x3fffffff;const int dx[4] = { 1, -1, 0, 0 };const int dy[4] = { 0, 0, 1, -1 };const double eps = 1e-8;const int maxn = 666;const int N = 1010;//点const int M = 2 * 10010;//边const int inf = 1000000000;struct Node {//边,点f到点t,流量为c,费用为wint f, t, c, w;}e[M];int next1[M], point[N], dis[N], q[N], pre[N], ne;//ne为已添加的边数,next,point为邻接表,dis为花费,pre为父亲节点bool u[N];void init();void add_edge(int f, int t, int d1, int d2, int w);void MCMF(int s, int t, int n, int &flow, int &cost);char crossword[506];//char word[106][506];//next数组(prefix数组)//输入待存的空数组next, 待匹配的数组p, 和p数组的长度lenvoid cptnext(int next[], char p[], int len) {int lolp = 0;//length of longest prefixnext[1] = 0;for (int nocm = 2; nocm < len + 1; nocm++) {while (lolp > 0 && p[lolp] != p[nocm - 1])lolp = next[lolp];if (p[lolp] == p[nocm - 1])lolp++;next[nocm] = lolp;}}int main() {//ios::sync_with_stdio(false);freopen("in.txt", "r", stdin);//freopen("out.txt", "w", stdout);int n;while (scanf("%d", &n) != EOF) {scanf("%s", crossword);init();int m;scanf("%d", &m);for (int i = 0; i < m; i++) {char word[506];scanf("%s", word);int p;scanf("%d", &p);int prefix[506];cptnext(prefix, word, strlen(word));//字符串匹配int nocm = 0;for (int j = 0; j < n; j++) {while (nocm > 0 && word[nocm] != crossword[j])nocm = prefix[nocm];if (word[nocm] == crossword[j])nocm++;if (nocm == strlen(word)) {//建一条匹配字符串头至尾的边add_edge(j - nocm + 1, j + 1, 1, 0, -p);}}}int x;scanf("%d", &x);for (int i = 0; i <= n; i++) {add_edge(i, i + 1, x, 0, 0);}int flow = 0, cost = -inf;MCMF(0, n + 1, n + 2, flow, cost);printf("%d\n", -cost);}return 0;}//建图前运行init()//节点下标从0开始//加边时运行add_edge(a,b,c,0,d)表示加一条a到b的流量为c花费为d的边(注意花费为单位流量花费)//特别注意双向边,运行add_edge(a,b,c,0,d),add_edge(b,a,c,0,d)较好,不要只运行一次add_edge(a,b,c,c,d),费用会不对。//求解时代入MCMF(s,t,n,v1,v2),表示起点为s,终点为t,点数为n的图中,最大流为v1,最大花费为v2void init(){memset(point, -1, sizeof(point));    //邻接表初始化      ne = 0;}//f到t的一条边,流量为d1,反向流量d2,花费w,反向边花费-w(可以反悔)void add_edge(int f, int t, int d1, int d2, int w){e[ne].f = f, e[ne].t = t, e[ne].c = d1, e[ne].w = w;        //最大流用dfs 自带回溯 所以不需要记录每一条边的起点  这里费用流多记录一个变量方便回溯修改流量。next1[ne] = point[f], point[f] = ne++;e[ne].f = t, e[ne].t = f, e[ne].c = d2, e[ne].w = -w;next1[ne] = point[t], point[t] = ne++;}bool spfa(int s, int t, int n){int i, tmp, l, r;memset(pre, -1, sizeof(pre));for (i = 0; i < n; ++i)dis[i] = inf;                           //最短路的初始化dis[s] = 0;q[0] = s;l = 0, r = 1;u[s] = true;while (l != r) {tmp = q[l];l = (l + 1) % (n + 1);                               //手写模拟循环队列u[tmp] = false;for (i = point[tmp]; i != -1; i = next1[i]) {if (e[i].c && dis[e[i].t] > dis[tmp] + e[i].w) {         //经典的循环队列模拟spfa  dis[e[i].t] = dis[tmp] + e[i].w;pre[e[i].t] = i;if (!u[e[i].t]) {u[e[i].t] = true;q[r] = e[i].t;r = (r + 1) % (n + 1);}}}}if (pre[t] == -1)return false;return true;}//起点s,终点t,点数n,最大流flow,最小花费cost//  该做法是比较常见的不建辅助网络  在原网络上每一次选择费用最小的路径进行增广   直到找不到可以增广的路径 void MCMF(int s, int t, int n, int &flow, int &cost) {int tmp, arg;flow = cost = 0;while (spfa(s, t, n))      //每一次如果求最短路的过程中访问到了汇点  那么pre[t]!=-1 就继续往下进行{arg = inf, tmp = t;while (tmp != s) {                            //从终点一直回溯到起点  求出路径上的最小流量arg = min(arg, e[pre[tmp]].c);tmp = e[pre[tmp]].f;}tmp = t;while (tmp != s) {                              //再次回溯  修改流量e[pre[tmp]].c -= arg;e[pre[tmp] ^ 1].c += arg;tmp = e[pre[tmp]].f;}flow += arg;                      //修改最大流cost += arg * dis[t];              //修改最小费用}}


1 0