网络流 求一个字典序最小的最小割

P3308 [SDOI2014]LIS

[SDOI2014]LIS(https://www.luogu.com.cn/problem/P3308)

题目描述

给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若干项,使得A的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。 如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。

输入格式

输入包含多组数据。

输入的第一行包含整数T,表示数据组数。接下来4*T行描述每组数据。

每组数据的第一行包含一个整数N,表示A的项数。

接下来三行,每行N个整数A1..An,B1.,Bn,C1..Cn,满足1 < =Ai,Bi,Ci < =10^9,且Ci两两不同。

输出格式

对每组数据,输出两行。第一行包含两个整数S,M,依次表示删去项的代价和与数量;接下来一行M个整数,表示删去项在A中的的位置,按升序输出。

输入输出样例

输入
1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1
输出
4 3
2 3 6

说明/提示

【样例说明】

解释:删去(A2,43,A6),(A1,A6),(A2,43,44,A5)等都是合法的方案,但{A2,43,A6)对应的C值的字典序最小。
1 < =N < =700 T < =5

思路:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1500 + 10;
const int INF = 0x3f3f3f3f;

//注释为弧优化
struct node {
    int form, to, cap, flow, next;
} edge[5000005];
int head[maxn];
int cnt;

struct max_Folw {
    int d[maxn], cur[maxn], start, tend;
    bool vis[maxn];

    void init() {
        memset(head, -1, sizeof(head));
        cnt=0;
    }

    void add(int start, int to, int cap) {
        edge[cnt].form = start;
        edge[cnt].to = to;
        edge[cnt].cap = cap;
        edge[cnt].flow = 0;
        edge[cnt].next = head[start];
        head[start] = cnt++;
    }

    void AddEdge(int start, int to, int cap) {
        //cout<<start<<" "<<to<<" "<<cap<<endl;
        add(start, to, cap);
        add(to, start, 0);
    }

    bool BFS() {
        memset(d, -1, sizeof(d));
        int Q[maxn * 2];
        int Thead, Ttail;
        Thead = Ttail = 0;
        Q[Ttail++] = tend;
        d[tend] = 0;
        while (Thead<Ttail) {
            int x = Q[Thead];
            if (x == start)
                return true;
            for (int i = head[x]; i != -1; i = edge[i].next) {
                int temp = edge[i].to;
                if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //没有标记,且可行流大于0
                    d[temp] = d[x] + 1;
                    Q[Ttail++] = temp;
                }
            }
            Thead++;
        }
        return false;//汇点是否成功标号,也就是说是否找到增广路
    }

    int DFS(int x, int cap) {
        if (x == tend)
            return cap;
        int flow = 0, f;
        //for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) {
        for (int i = head[x]; i != -1; i = edge[i].next) {
            int temp = edge[i].to;
            if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) {
                f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow));
                edge[i].flow += f;
                edge[i ^ 1].flow -= f;
                flow += f;
                if (flow == cap)
                    return flow;
            }
        }
        d[x] = -2;//防止重搜
        return flow;
    }

    int maxflow(int s, int t) {
        start=s, tend=t;
        int flow = 0, f;
        while (BFS()) {
            //memcpy(cur, head, sizeof head);
            flow += DFS(start, INF);
        }
        return flow;
    }

    queue<int> q;
    int bfs(int s, int t){
        memset(vis, 0, sizeof(vis));
        while(!q.empty()) q.pop();

        q.push(s); vis[s]=1;
        while(!q.empty()){
            int u=q.front(); q.pop();
            for(int i=head[u]; i!=-1; i=edge[i].next){
                int to=edge[i].to;
                if(edge[i].cap-edge[i].flow!=0&&vis[to]==0){
                    q.push(to); vis[to]=1;
                    if(to==t) return 1;
                }
            }
        }
        return 0;
    }


} flow;

int a[1505], b[1505];
struct Node{
    int c, id;
}c[1505];
int f[1505], id[1505];
vector<int> ans;
int main(){

    int t; scanf("%d", &t);
    while(t--){
        memset(f, 0, sizeof(f));
        memset(id, 0, sizeof(id));
        ans.clear();
        int n; scanf("%d", &n);
        for(int i=1; i<=n; i++){
            scanf("%d", &a[i]);
        }
        for(int i=1; i<=n; i++){
            scanf("%d", &b[i]);
        }
        for(int i=1; i<=n; i++){
            scanf("%d", &c[i].c);
            c[i].id=i;
        }
        int mx=0;
        for(int i=1; i<=n; i++){
            f[i]=1;
            for(int j=i-1; j>=1; j--){
                if(a[j]<a[i]){
                    f[i]=max(f[i], f[j]+1);
                    mx=max(mx, f[i]);
                }
            }
        }
        flow.init();
        for(int i=1; i<=n; i++){
            flow.AddEdge(i, i+n, b[i]);
            id[i]=cnt-2;
            //cout<<i<<"="<<edge[id[i]].form<<" "<<edge[id[i]].to<<endl;
            if(f[i]==1){
                flow.AddEdge(0, i, INF);
            }
            if(f[i]==mx){
                flow.AddEdge(i+n, 2*n+1, INF);
            }
            for(int j=i+1; j<=n; j++){
                if(a[i]<a[j]&&f[i]+1==f[j]){
                    flow.AddEdge(i+n, j, INF);
                }
            }
        }
        printf("%d ", flow.maxflow(0, 2*n+1));

        sort(c+1, c+n+1, [](Node &a, Node &b){return a.c<b.c;});
        for(int i=1; i<=n; i++){
            int x=c[i].id, y=c[i].id+n;
            if(edge[id[c[i].id]].cap-edge[id[c[i].id]].flow==0&&!flow.bfs(x, y)){
                ans.push_back(c[i].id);
                 
                //退流
                flow.maxflow(2*n+1, y);
                flow.maxflow(x, 0);
                edge[id[c[i].id]].cap=edge[id[c[i].id]].flow=0;
                edge[id[c[i].id]^1].cap=edge[id[c[i].id]^1].flow=0;
            }
        }
        printf("%d\n", ans.size());
        sort(ans.begin(), ans.end());
        for(auto x: ans){
            printf("%d ", x);
        }
        printf("\n");
    }

    return 0;
}

posted @   liweihang  阅读(254)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『网络流 求一个字典序最小的最小割』
点击右上角即可分享
微信分享提示