bzoj3550

费用流+线性规划

搞了很长时间。。。

我们可以设立式子,a[1]+a[2]+a[3]+...+a[n]<=k , ... , a[2 * n + 1]+ ... +a[3*n]<=k

a是指该位有没有选

那么我们添加一个辅助变量f

a[1]+a[2]+a[3]+...+a[n]+f[1]=k,

...

a[2*n+1]+...+a[3*n]+f[2*n+1]=k

我们就得到了2n+1个式子

然后我们添加两个式子a(0):0=0 a(2*n+2):0=0

然后差分得到2n+2个式子,后一个式子减前一个式子

0:a[1]+a[2]+...+a[n]+f[1]=k

1:a[2]+...+a[n+1]+f[2]-a[1]-...-a[n]-f[1]=0->a[n+1]+f[2]-a[1]-f[1]=0

2:a[n+2]+f[3]-a[2]-f[2]=0

...

2n+1:a[3*n]+f[2*n+1]-a[2*n]-f[2*n]

2n+2:a[2n+1]+...+a[3*n]+f[2*n+1]=k

然后这个式子很像网络流的流量平衡,于是这样建图,正对负,负对正,因为第0项是a[1]+...+a[n],所以0->[1,n],因为都是这样形式的a[n+1]+f[2]-a[1]-f[1],-a[1]和+a[1]对应,因为a∈[0,1],所以流量为1,a[i]=1是指选了a[i],所以费用为a[i]

i-1->i,因为相邻两项-f[i],+f[i],f[i]∈[0,k],因为是辅助变量,所以什么都没有对应,流量为k,费用为0

后面类似 然后就建好图了 设立源汇和1n相连,流量为k 感觉理解不够深刻 碰见这种序列+限制的题可以用费用流做,先把限制代数化,然后-+连边,一般还会相邻的点之间连边,这样可以解决一些序列的问题,以后碰见再做

#include<bits/stdc++.h>
using namespace std;
const int N = 1010, inf = 0x3f3f3f3f;
struct edge {
    int nxt, to, f, c;
} e[N * 50];
int n, m, k, source, sink, tot, cnt = 1, sum;
int a[N], head[N], d[N], pree[N], prev[N], vis[N], live[N], dead[N], day[N], c[N], l[N], p[N];
inline void link(int u, int v, int f, int c)
{
    e[++cnt].nxt = head[u];
    head[u] = cnt;
    e[cnt].f = f;
    e[cnt].to = v;
    e[cnt].c = c;
}
inline void insert(int u, int v, int f, int c)
{
    link(u, v, f, c);
    link(v, u, 0, -c);
}
bool spfa()
{
    memset(d, -1, sizeof(d));
    d[source] = 0;
    queue<int> q;
    q.push(source);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && (d[e[i].to] < d[u] + e[i].c || d[e[i].to] == -1))
        {
            pree[e[i].to] = i;
            prev[e[i].to] = u;
            d[e[i].to] = d[u] + e[i].c;
            if(vis[e[i].to] == 0)
            {
                q.push(e[i].to);
                vis[e[i].to] = 1;
            }
        }
    }
    return d[sink] != -1; 
}
inline int Edmonds_Karp()
{
    int ans = 0;
    while(spfa())
    {
        int now = sink, delta = inf;
        while(now != source)
        {
            delta = min(delta, e[pree[now]].f);
            now = prev[now];
        }
        now = sink;
        while(now != source)
        {
            e[pree[now]].f -= delta;
            e[pree[now] ^ 1].f += delta; 
            now = prev[now];
        }
        ans += delta * d[sink];
    }
    return ans;
}
int main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= 3 * n; ++i) scanf("%d", &a[i]);
    source = 2 * n + 2;
    sink = 2 * n + 3;
    int s = 0, t = 2 * n + 1; 
    insert(source, s, k, 0);
    insert(t, sink, k, 0);
    sink = 2 * n + 1;
    for(int i = 1; i <= n; ++i) insert(s, i, 1, a[i]);
    for(int i = 1; i <= 2 * n + 1; ++i) insert(i - 1, i, k, 0);
    for(int i = n + 1; i <= 2 * n; ++i) insert(i, t, 1, a[i + n]);
    for(int i = n + 1; i <= 2 * n; ++i) insert(i - n, i, 1, a[i]);    

/*    for(int i = 1; i <= 2 * n + 1; ++i) insert(i - 1, i, k, 0);
    for(int i = 1; i <= n; ++i) 
    {
        insert(s, i, 1, a[i]);
        insert(i, i + n, 1, a[i + n]);
        insert(i + n, t, 1, a[i + 2 * n]);
    } */
    printf("%d\n", Edmonds_Karp());
    return 0;
} 
View Code

 

posted @ 2017-08-12 16:51  19992147  阅读(191)  评论(0编辑  收藏  举报