【BZOJ2893】征服王

本文介绍了解决可相交最小路径覆盖问题的两种方法:一种利用二分图匹配算法,通过增加边来确保可达性的正确合并次数;另一种采用最大费用最大流算法进行贪心模拟。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

传送门


题解

第一步自然是缩点变成无向图。
然后就是裸的可相交最小路径覆盖。

不可相交最小路径覆盖

即用最少的路径覆盖图中所有点, 路径不可相交。

可用最小割解决。
考虑把原图中的每一个点看成一条单独的路径, 接着不断将路径合并, 形成更长的路径。
那么最后的路径数 = 总点数 - 合并次数\((\)没合并次减少一条路径\()\), 要使路径数最小就要让合并次数最多。
可用二分图匹配计算。
把原图中的一个点\(u\), 拆成\(u_1\)\(u_2\);把原图中的边\((u, v)\), 改成\((u_1, v_2)\)。 那么就构成了一个二分图。在二分图中加一条匹配边等于一次合并, 最大匹配即为最大合并次数。

可相交最小路径覆盖

做法一:
即用最少的路径覆盖图中所有点, 路径可相交。

考虑把在上一个问题的算法直接搬过来。 显然是错的, 因为如果两个点的路径中的任意一个点有被覆盖过, 则两个点就会被无法被合并。
我们要消除这种影响。 换句话说, 只要\(A\)可到达\(B\), 那么在二分图就要有一条\((A_1, B_2)\)的边。

然后在求二分图匹配, 即可得到正确的合并次数。

做法二:
最大费用最大流摸拟贪心。


代码

做法一:

#include <iostream>
#include <cstdlib>
#include <stack>
#include <cstdio>
#include <cstring>
#include <bitset>
#include <queue>

using namespace std;

const int N = 1210, M = 200010;

const int INF = 0x3F3F3F3F;

struct edge
{   int from, to, flow, cap;
    edge() { }
    edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), flow(_3), cap(_4) { }
};

namespace Graph
{   edge edges[M];

    int head[N], nxt[M], tot;

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

    inline void add_edge(int x, int y)
    {       edges[tot] = edge(x, y, 0, 0);
        nxt[tot] = head[x];
        head[x] = tot++;
    }
}

namespace DAG
{   edge edges[M];

    int head[N], nxt[M], tot;

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

    inline void add_edge(int x, int y)
    {   edges[tot] = edge(x, y, 0, 0);
        nxt[tot] = head[x];
        head[x] = tot++;
    }
}

struct Dinic
{   edge edges[N * N * 2];
    int head[2 * N], nxt[N * N * 2], tot;

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

    inline void add_edge(int x, int y, int z)
    {   edges[tot] = edge(x, y, 0, z);
        nxt[tot] = head[x];
        head[x] = tot++;
        edges[tot] = edge(y, x, 0, 0);
        nxt[tot] = head[y];
        head[y] = tot++;
    }

    int L, R;

    int s, t;

    int d[2 * N];

    bool bfs()
    {   memset(d, -1, sizeof(d));
        queue<int> q;
        q.push(s);
        d[s] = 0;
        while (!q.empty())
        {   int x = q.front(); q.pop();
            for (int i = head[x]; ~i; i = nxt[i])
            {   edge & e = edges[i];
                if (e.cap > e.flow && d[e.to] == -1)
                {   d[e.to] = d[x] + 1;
                    q.push(e.to);
                }
            }
        }
        return d[t] != -1;
    }

    int cur[2 * N];

    int dfs(int x, int a)
    {   if (x == t || a == 0) return a;
        int f, flow = 0;
        for (int & i = cur[x]; ~i; i = nxt[i])
        {   edge & e = edges[i];
            if (d[e.to] == d[x] + 1 && (f = dfs(e.to, min(e.cap-e.flow, a))) > 0)
            {   e.flow += f;
                edges[i^1].flow -= f;
                flow += f;
                a -= f;
                if (a == 0) break;
            }
        }
        return flow;
    }

    int maxflow(int _s, int _t)
    {   s = _s, t = _t;
        int flow = 0;
        while (bfs())
        {   for (int i = L; i <= R; i++)
                cur[i] = head[i];
            flow += dfs(s, INF);
        }
        return flow;
    }
} dinic;

int n, m;

bool bo1[N], bo2[N], boo1[N], boo2[N];

int dfn[N], low[N], dfs_clock;
int num[N], scc;
stack<int> st;

bool boo[N], bo[N];

void Tarjan(int x)
{   using namespace Graph;
    low[x] = dfn[x] = ++dfs_clock;
    st.push(x);
    for (int i = head[x]; ~i; i = nxt[i])
    {   edge & e = edges[i];
        if (!dfn[e.to])
        {   Tarjan(e.to);
            low[x] = min(low[x], low[e.to]);
        }
        else if (!num[e.to]) low[x] = min(low[x], dfn[e.to]);
    }
    if (low[x] == dfn[x])
    {   int v;
        ++scc;
        do
        {   v = st.top(); st.pop();
            num[v] = scc;
            if (boo1[v]) bo1[scc] = 1;
            if (boo2[v]) bo2[scc] = 1;
        }
        while(v != x);
    }
}

int out[N], in[N];

int Ans;

queue<int> q;

int node[N], total;

bitset<N> f[N];

void solve()
{   using namespace DAG;
    for (int i = 1; i <= scc; i++)
        if (in[i] == 0 && !bo1[i])
        {   puts("no solution");
            return;
        }
    for (int i = 1; i <= scc; i++)
        if (out[i] == 0 && !bo2[i])
        {   puts("no solution");
            return;
        }
    total = 0;
    while (!q.empty()) q.pop();
    for (int i = 1; i <= scc; i++)
        if (in[i] == 0)
            q.push(i), node[++total] = i;
    while (!q.empty())
    {   int x = q.front(); q.pop();
        for (int i = head[x]; ~i; i = nxt[i])
        {   edge & e = edges[i];
            if ((--in[e.to]) == 0)
                q.push(e.to),
                node[++total] = e.to;
        }
    }
    int S = 2 * scc + 1, T = 2 * scc + 2;
    dinic.L = 0, dinic.R = T;
    dinic.init();
    for (int i = 1; i <= scc; i++)
        dinic.add_edge(S, i, 1);
    for (int i = 1; i <= scc; i++)
        dinic.add_edge(i+scc, T, 1);
    for (int i = total; i >= 1; i--)
    {   int now = node[i];
        f[now][now-1] = 1;
        for (int j = head[now]; ~j; j = nxt[j])
        {   edge & e = edges[j];
            f[now] |= f[e.to];
        }
        for (int j = 1; j <= scc; j++)
            if (f[now][j-1] && j != now)
                dinic.add_edge(now, j+scc, 1);
    }
    for (int i = 1; i <= n; i++)
        f[i].reset();
    printf("%d\n", scc - dinic.maxflow(S, T));
}

void build_DAG()
{   DAG::init();
    for (int i = 1; i <= n; i++)
    {   for (int j = Graph::head[i]; ~j; j = Graph::nxt[j])
        {   edge & e = Graph::edges[j];
            if (num[i] != num[e.to])
                DAG::add_edge(num[i], num[e.to]),
                in[num[e.to]]++,
                out[num[i]]++;
        }
    }
}

void clear()
{   memset(boo1, 0, sizeof(boo1));
    memset(boo2, 0, sizeof(boo2));
    memset(bo1, 0, sizeof(bo1));
    memset(bo2, 0, sizeof(bo2));
    memset(num, 0, sizeof(num));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    dfs_clock = 0;
    scc = 0;
    while (!st.empty()) st.pop();
}

int main()
{   int T;
    scanf("%d", &T);
    while (T--)
    {   int a, b;
        scanf("%d %d %d %d", &n, &m, &a, &b);
        clear();
        for (int i = 1; i <= a; i++)
        {   int x;
            scanf("%d", &x);
            boo1[x] = 1;
        }
        for (int i = 1; i <= b; i++)
        {   int x;
            scanf("%d", &x);
            boo2[x] = 1;
        }
        Ans = 0;
        Graph::init();
        for (int i = 1; i <= m; i++)
        {   int x, y;
            scanf("%d %d", &x, &y);
            Graph::add_edge(x, y);
        }
        for (int i = 1; i <= n; i++)
            if (!dfn[i]) Tarjan(i);
        build_DAG();
        solve();
    }
    return 0;
}

做法二:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

#include <queue>
#include <stack>

using namespace std;


const int N = 1010, M = 10010;

const int INF = 0x3F3F3F3F;


struct edge
{
    int from, to, flow, cap, dis;
    edge() { }
    edge(int _1, int _2, int _3, int _4, int _5) : from(_1), to(_2), flow(_3), cap(_4), dis(_5) { }
};


namespace Graph
{
    edge edges[2*M];
    int head[N], nxt[2*M], tot;
    
    inline void init()
    {
        memset(head, -1, sizeof(head));
        tot = 0;
    }

    inline void add_edge(int x, int y)
    {
        edges[tot] = edge(x, y, 0, 0, 0);
        nxt[tot] = head[x];
        head[x] = tot++;
    }
}


struct MCMF
{
    edge edges[2 * N * N];
    int head[2*N], nxt[2 * N * N], tot;

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

    inline void add_edge(int x, int y, int z, int k)
    {
        edges[tot] = edge(x, y, 0, z, k);
        nxt[tot] = head[x];
        head[x] = tot++;
        edges[tot] = edge(y, x, 0, 0, -k);
        nxt[tot] = head[y];
        head[y] = tot++;
    }

    int s, t;

    int L, R;


    int dist[2*N]; bool inq[2*N];

    int a[2*N], p[2*N];

    bool SPFA()
    {
        for (int i = L; i <= R; i++)
            dist[i] = -INF, inq[i] = 0;
        
        queue <int> q;
        q.push(s);
        inq[s] = 1;
        dist[s] = 0;
        a[s] = INF;
        p[s] = 0;
        
        while (!q.empty())
        {
            int x = q.front(); q.pop();
            inq[x] = 0;
            
            for (int i = head[x]; ~i; i = nxt[i])
            {
                edge & e = edges[i];
                if (e.cap > e.flow && dist[e.to] < dist[x] + e.dis)
                {
                    dist[e.to] = dist[x] + e.dis;
                    a[e.to] = min(a[x], e.cap - e.flow);
                    p[e.to] = i;
                    if (!inq[e.to]) q.push(e.to), inq[e.to] = 1;
                }
            }
        }
        
        if (dist[t] <= 0) return 0;
        
        for (int i = t; i != s; i = edges[p[i]].from)
        {
            edges[p[i]].flow += a[t];
            edges[p[i]^1].flow -= a[t];
        }
        
        return 1;
    }

    int calc(int _s, int _t)
    {
        int Ans = 0;
        s = _s, t = _t;
        while (SPFA()) Ans++;
        return Ans;
    }
} maxcostminflow;


int n, m, a, b;

bool type1[N], type2[N];


bool bo1[N], bo2[N];

int low[N], dfn[N], dfs_clock;
int bel[N], cnt;
stack <int> stk;

void Tarjan(int x)
{
    using namespace Graph;
    
    low[x] = dfn[x] = ++dfs_clock;
    stk.push(x);
    
    for (int i = head[x]; ~i; i = nxt[i])
    {
        edge & e = edges[i];
        
        if (!dfn[e.to])
        {
            Tarjan(e.to);
            low[x] = min(low[x], low[e.to]);
        }
        else if (!bel[e.to]) low[x] = min(low[x], dfn[e.to]);
    }
    
    if (low[x] == dfn[x])
    {
        int v = 0;
        cnt++;
        do
        {
            v = stk.top(); stk.pop();
            bel[v] = cnt;
            if (type1[v]) bo1[cnt] = 1;
            if (type2[v]) bo2[cnt] = 1;
        }
        while (v != x);
    }
}


bool g[N][N];

int deg1[N], deg2[N];

void solve()
{
    memset(dfn, 0, sizeof(dfn));
    memset(bel, 0, sizeof(bel));
    memset(g, 0, sizeof(g));
    memset(bo1, 0, sizeof(bo1));
    memset(bo2, 0, sizeof(bo2));
    
    cnt = 0;
    dfs_clock = 0;
    
    for (int i = 1; i <= n; i++)
        if (!dfn[i]) Tarjan(i);
    
    for (int i = 0; i < Graph::tot; i++)
    {
        edge & e = Graph::edges[i];
        if (bel[e.from] != bel[e.to]) g[bel[e.from]][bel[e.to]] = 1;
    }
    
    memset(deg1, 0, sizeof(deg1));
    memset(deg2, 0, sizeof(deg2));
    
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= cnt; j++)
            if (i != j && g[i][j])
                deg2[i]++, deg1[j]++;
    
    for (int i = 1; i <= cnt; i++)
        if (!deg1[i] && !bo1[i]) return void(puts("no solution"));
    for (int i = 1; i <= cnt; i++)
        if (!deg2[i] && !bo2[i]) return void(puts("no solution"));
    
    maxcostminflow.init();
    
    maxcostminflow.L = 0, maxcostminflow.R = 2 * cnt + 2;   
    
    int S = cnt + cnt + 1, T = cnt + cnt + 2;
    
    for (int i = 1; i <= cnt; i++)
        maxcostminflow.add_edge(S, i, INF, 0);
    for (int i = 1; i <= cnt; i++)
        maxcostminflow.add_edge(i+cnt, T, INF, 0);
    
    for (int i = 1; i <= cnt; i++)
    {
        maxcostminflow.add_edge(i, i+cnt, 1, 1);
        maxcostminflow.add_edge(i, i+cnt, INF, 0);
    }
    
    for (int i = 1; i <= cnt; i++)
        for (int j = 1; j <= n; j++)
            if (g[i][j])
                maxcostminflow.add_edge(i+cnt, j, INF, 0);
    
    printf("%d\n", maxcostminflow.calc(S, T));
}


int main()
{
    int T;
    scanf("%d", &T);
    
    while (T--)
    {
        memset(type1, 0, sizeof(type1));
        memset(type2, 0, sizeof(type2));
        
        scanf("%d %d %d %d", &n, &m, &a, &b);
        
        Graph::init();
        
        for (int i = 1; i <= a; i++)
        {
            int x;
            scanf("%d", &x);
            type1[x] = 1;
        }
        
        for (int i = 1; i <= b; i++)
        {
            int x;
            scanf("%d", &x);
            type2[x] = 1;
        }
        
        for (int i = 1; i <= m; i++)
        {
            int x, y;
            scanf("%d %d", &x, &y);
            Graph::add_edge(x, y);
        }
        
        solve();
    }
    
    return 0;
}

转载于:https://www.cnblogs.com/2016gdgzoi509/p/10632085.html

内容概要:该研究通过在黑龙江省某示范村进行24小时实地测试,比较了燃煤炉具与自动/手动进料生物质炉具的污染物排放特征。结果显示,生物质炉具相比燃煤炉具显著降低了PM2.5、CO和SO2的排放(自动进料分别降低41.2%、54.3%、40.0%;手动进料降低35.3%、22.1%、20.0%),但NOx排放未降低甚至有所增加。研究还发现,经济性和便利性是影响生物质炉具推广的重要因素。该研究不仅提供了实际排放数据支持,还通过Python代码详细复现了排放特征比较、减排效果计算和结果可视化,进一步探讨了燃料性质、动态排放特征、碳平衡计算以及政策建议。 适合人群:从事环境科学研究的学者、政府环保部门工作人员、能源政策制定者、关注农村能源转型的社会人士。 使用场景及目标:①评估生物质炉具在农村地区的推广潜力;②为政策制定者提供科学依据,优化补贴政策;③帮助研究人员深入了解生物质炉具的排放特征和技术改进方向;④为企业研发更高效的生物质炉具提供参考。 其他说明:该研究通过大量数据分析和模拟,揭示了生物质炉具在实际应用中的优点和挑战,特别是NOx排放增加的问题。研究还提出了多项具体的技术改进方向和政策建议,如优化进料方式、提高热效率、建设本地颗粒厂等,为生物质炉具的广泛推广提供了可行路径。此外,研究还开发了一个智能政策建议生成系统,可以根据不同地区的特征定制化生成政策建议,为农村能源转型提供了有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值