• HDU 5406 CRB and Apple 花样费用流向解法


    题意简化一下就是一个序列,找出两个最大不下降子序列使得他们的长度和最长。

    = =作为一个DP渣,状态设计大概也就到了dp[i][j]表示第一个人最后一次取到i,第二个人取到j这个地方了。。怎么在可行复杂度内转移?不会啊望天。。

    其实作为图论工作者第一反应是费用流,但是边数太多了没敢搞= =

    然而其实费用流也是可以过的。(x, y)表示容量为x费用为y,

    建图很简单,建源s和汇e,加一个点t限制从 源最多流量是2,也就是s->t(2, 0)。将各个点拆掉限制只能取一次,x->x'(1, 1),容量1限制只能取一次,费用表示长度+1

    t->x(1, 0) 可以从任意点开始。 x'->e(1, 0) 可以从任意一个点结束。

    跑最大费用最大流,然后就会T(喂)

    记得POJ有一道题卡SPFA,但是改成栈就能过= = 

    于是你顺手把它改成栈发现居然不T了(从一开始接触ACM,这个问题就一直困扰着我T T怎么才能构造出卡队列不卡栈的数据?T T求指导)

    代码1,大概1400ms 左右。

    #include<cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #include <stack>
    using namespace std;
    
    const int N  = 2010;
    const int M = 4000005;
    const int INF = 0x3f3f3f3f;
    struct point{
        int u,v, next, flow, cost;
        point(){};
        point(int x, int y , int z, int f, int c){
            u = x,  v=  y;  next = z,   flow = f, cost = c;
        };
    }p[M];
    int pre[N], head[N], d[N];
    bool vis[N];
    int s, e, no;
    struct Info{
        int h, d;
        void scan(){
            scanf("%d%d", &h, &d);
        }
        bool operator<(const Info &I)const{
            if(h == I.h)    return d < I.d;
            return h > I.h;
        }
    }info[N];
    bool spfa(){
        stack<int>q;
        int x, y, now, i;
        memset(d, 0xc0, sizeof(d));
        memset(vis, 0, sizeof(vis));
        memset(pre, -1, sizeof(pre));
        q.push(s);  d[s] = 0;   vis[s] = 1;
        while(!q.empty()){
            x = q.top();  q.pop();
            vis[x] = 0;
            for(i = head[x]; i != -1; i = p[i].next){
                y = p[i].v;
                if(p[i].flow && d[y] < d[x] + p[i].cost){
                    d[y] = d[x] + p[i].cost;
                    pre[y] = i;
                    if(!vis[y]){
                        q.push(y);
                        vis[y] = 1;
                    }
                }
            }
        }
        return (d[e] != d[e+1]);
    }
    
    int mcmf(){
        int maxflow = 0, i, minflow, mincost = 0;
        while(spfa()){
            minflow = INF + 1;
            for(i = pre[e]; i != -1; i = pre[p[i].u]) {
                if(p[i].flow < minflow)
                    minflow = p[i].flow;
            }
            for(i = pre[e]; i != -1; i = pre[p[i].u]){
                p[i].flow -= minflow;
                p[i ^ 1].flow += minflow;
            }
            mincost += d[e] * minflow;
        }
        return mincost;
    }
    
    void init(){
        memset(head, -1, sizeof(head));
        no = 0;
    }
    void add(int x, int y, int f, int co){
        p[no] = point(x, y, head[x], f, co);    head[x] = no++;
        p[no] = point(y, x, head[y], 0, -co);   head[y] = no++;
    }
    int main(){
        int TC, n, i, j;
        scanf("%d", &TC);
        while(TC--){
            scanf("%d", &n);
            init();s = 0;  e = 2 * n + 2;
            for(i = 1; i <= n; i++){
                info[i].scan();
                add(i<<1, i << 1| 1, 1, 1);
                add(1, i << 1, 1, 0);
                add(i << 1 | 1, e, 1, 0);
            }
            sort(info + 1, info + n + 1);
            for(i = 1; i <= n; i++){
                for(j = i + 1; j <= n; j++){
                    if(info[i].d <= info[j].d){
                        add(i<<1|1, j <<1, 1, 0);
                    }
                }
            }
            add(s, 1, 2, 0);
    
            printf("%d
    ", mcmf());
        }
        return 0;
    }
    View Code

    接下来进入各种尝试优化阶段。

    一开始企图改进建边的方式,具体见 http://blog.csdn.net/mxymxy1994mxy/article/details/47818397 这位大神的说法,空间省了很多,然而用普通队列搞还是T(当然也可能是我蠢(然而显然不是可能,事实上是就是我蠢,趴(为什么最近这么想吐槽自己))),单纯形是厉害ORZ,然而改成栈的SPFA,不到500ms就过了,是厉害啊。

    代码2,其他都一样,修改了一下建边的方式

            init();s = 0;  e = 2 * n + 2;
            for(i = 1; i <= n; i++){
                info[i].scan();
                add(i<<1, i << 1| 1, 1, -1);
                add(1, i << 1, 1, 0);
                add(i << 1 | 1, e, 1, 0);
            }
            sort(info + 1, info + n + 1);
            int maxn;
            for(i = 1; i <= n; i++){
                maxn = INF;
                for(j = i + 1; j <= n; j++){
                    if(info[j].d < info[i].d ){
                        continue;
                    }
                    if(info[j].d <= maxn)   {
                        add(i << 1| 1 | 1, j << 1, 1, 0);
                        add(i << 1, j << 1, 1, 0);
                       // flag = false;
                        maxn = info[j].d;
                    }
    
                }
            }
            add(s, 1, 2, 0);
    View Code

    由于悲剧主要出于SPFA,企图用ZKW改一下,但好像不行。。。

    T________T

    就这样吧,还是学DP去

  • 相关阅读:
    Mysql 数据库高级
    Mysql 数据库
    并发编程
    网络编程
    1113
    1112
    P相遇游戏
    中位数
    PETS
    打暴力程序的正确做法
  • 原文地址:https://www.cnblogs.com/bbbbbq/p/4748992.html
Copyright © 2020-2023  润新知