Input
第一行包含两个正整数n,m,分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。
第二行包含n个正整数,其中第k个数ak表示第k份寿司的代号。
接下来n行,第i行包含n-i+1个整数,其中第j个数di,i+j-1表示吃掉寿司能
获得的相应的美味度,具体含义见问题描述。
N<=100,Ai<=1000
Output
输出共一行包含一个正整数,表示Kiana能获得的总美味度减去花费的总钱数的最大值。
Sample Input
3 1
2 3 2
5 -10 15
-10 15
15
2 3 2
5 -10 15
-10 15
15
Sample Output
12
分析:看出来这是一道网络流题,却不知道该如何建模......
看到mx^2 + cx这个式子,我想起了bzoj1449的费用递增模型. 然而m和x都是固定的,每次的增量都是x,费用并不是递增的......
然后我又想:选取若干个不同的寿司,它们之间会产生贡献. 似乎可以用最小割来做,类似bzoj3144的离散变量模型吗? 这道题选择的寿司之间没有任何限制,当然不是离散变量模型了.
单纯考虑最小割可以吗? 每次要求选一段连续的区间不好处理啊.
上述尝试均失败后,果断打了暴力.
如果把区间看作点,这道题差不多就做完了. 选择了区间[l,r],则必然会选择在[l,r]中的所有的点,这实际上就是最大权闭合子图模型. 每个区间要向所有区间内的点连边吗? 不用!利用传递关系:[l,r]向[l + 1,r]和[l,r - 1]连边. 当l == r时,就代表了一个点.
费用要怎么处理呢? 将mx^2 + cx看作mx^2 和 cx两部分. 把编号也看作点. 如果一个编号被选择了,那么它的费用就是mx^2,不会改变. 如果一个点被选择了,那么它的费用就是它的编号. 所以每个编号向T连边,容量为m * i^2. 每个点向其所属的编号连边, 容量为inf. 每个点向T连边,容量为其编号. 这样的话选择了点i,就要割掉它到T的所有连边,即i的编号与T的连边和i与T的连边.
具体的建图方式如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6787c3749d435b72b16309a4000c7b70.png)
(参考sliverNebula的博客)
可以这么理解:割掉S与点i的边,就是放弃了i. 割掉i与T的连边,就是选i的代价.
做网络流的题要有把所有东西都看作“点”的想法.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 20010,maxm = 120010,inf = 0x7fffffff; int n,m,a[110],mx,d[110][110],cnt,pos[110][110],S,T,ans,head[maxn]; int to[maxm],nextt[maxm],w[maxm],tot = 2,vis[maxn],cur[maxn]; void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; w[tot] = 0; to[tot] = x; nextt[tot] = head[y]; head[y] = tot++; } bool bfs() { queue <int> q; q.push(S); memset(vis,-1,sizeof(vis)); vis[S] = 0; while (!q.empty()) { int u = q.front(); q.pop(); if (u == T) return true; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (w[i] && vis[v] == -1) { vis[v] = vis[u] + 1; q.push(v); } } } return false; } int dfs(int u,int f) { if (u == T) return f; int res = 0; for (int i = cur[u];i;i = nextt[i]) { int v = to[i]; if (w[i] && vis[v] == vis[u] + 1) { int temp = dfs(v,min(f - res,w[i])); w[i] -= temp; w[i ^ 1] += temp; res += temp; if (w[i]) cur[u] = i; if (res == f) return res; } } if (!res) vis[u] = -1; return res; } void dinic() { while (bfs()) { for (int i = 1; i <= T; i++) cur[i] = head[i]; ans -= dfs(S,inf); } } int main() { scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); mx = max(mx,a[i]); } cnt = mx + n; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) { if (i == j) pos[i][j] = i; else pos[i][j] = ++cnt; } S = cnt + 1; T = S + 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= n - i + 1; j++) { scanf("%d",&d[i][i + j - 1]); int l = i,r = i + j - 1; if (d[l][r] < 0) add(pos[l][r],T,-d[l][r]); else { add(S,pos[l][r],d[l][r]); ans += d[l][r]; } if (l != r) add(pos[l][r],pos[l + 1][r],inf),add(pos[l][r],pos[l][r - 1],inf); else { add(pos[l][r],a[l] + n,inf); add(pos[l][r],T,a[l]); } } for (int i = 1; i <= mx; i++) add(i + n,T,i * i * m); dinic(); printf("%d\n",ans); return 0; }