abc374 g

很容易想到建图,初始想法为,建完图后,求一个最小路径覆盖,但因为整个图不是DAG,所以需要缩点,但路径覆盖有两种说法,一种是最小不相交路径覆盖,另一种是最小可相交路径覆盖。

对于最小不相交路径覆盖,我们可以采用求二分图最大匹配来解决,因为每个点最多只有一个前驱节点和后继节点,因此将一个点拆成两个,即可转换为二分图。

对于最小可相交路径覆盖,每个点的前驱节点和后继节点的个数不在保证至多为1,所以我们需要先进行一遍传递闭包,例如a->b->c->d,我们对于中间的不关心,我们只关心最终的结果,即a可以到达d,中间的过程我们不关心,因为可以重复覆盖。由此就转化为了最小不相交路径覆盖问题。

对于建图,我们不能将字母和字母之间直接建图,例如 A B AB AB,我们就建一条从 A A A到 B B B的边,这样是不可行的,有以下反例:ab,bc,ac,如果我们按照上述进行建图,我们最终得到的答案为1,因为我们只需要一个abc的字符串即可,但对于字符串ac,其并没有作为子串进行出现。

所以正确的建图方式为:双重循环遍历,当两个字符串,其中一个开头的字母和另一个结尾的字母相同时,我们就再这两个串之间建一条边即可。

cpp 复制代码
 #include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair<ll, ll> pll;
typedef array<int,3> ar;
// #define endl "\n"
int mod=998244353;

constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
    struct _Edge {
        int to;
        T cap;
        _Edge(int to, T cap) : to(to), cap(cap) {}
    };
    
    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<int> cur, h;
    
    MaxFlow() {}
    MaxFlow(int n) {
        init(n);
    }
    
    void init(int n) {
        this->n = n;
        e.clear();
        g.assign(n, {});
        cur.resize(n);
        h.resize(n);
    }
    
    bool bfs(int s, int t) {
        h.assign(n, -1);
        std::queue<int> que;
        h[s] = 0;
        que.push(s);
        while (!que.empty()) {
            const int u = que.front();
            que.pop();
            for (int i : g[u]) {
                auto [v, c] = e[i];
                if (c > 0 && h[v] == -1) {
                    h[v] = h[u] + 1;
                    if (v == t) {
                        return true;
                    }
                    que.push(v);
                }
            }
        }
        return false;
    }
    
    T dfs(int u, int t, T f) {
        if (u == t) {
            return f;
        }
        auto r = f;
        for (int &i = cur[u]; i < int(g[u].size()); ++i) {
            const int j = g[u][i];
            auto [v, c] = e[j];
            if (c > 0 && h[v] == h[u] + 1) {
                auto a = dfs(v, t, std::min(r, c));
                e[j].cap -= a;
                e[j ^ 1].cap += a;
                r -= a;
                if (r == 0) {
                    return f;
                }
            }
        }
        return f - r;
    }
    void addEdge(int u, int v, T c) {
        g[u].push_back(e.size());
        e.emplace_back(v, c);
        g[v].push_back(e.size());
        e.emplace_back(u, 0);
    }
    T flow(int s, int t) {
        T ans = 0;
        while (bfs(s, t)) {
            cur.assign(n, 0);
            ans += dfs(s, t, std::numeric_limits<T>::max());
        }
        return ans;
    }
    
    std::vector<bool> minCut() {
        std::vector<bool> c(n);
        for (int i = 0; i < n; i++) {
            c[i] = (h[i] != -1);
        }
        return c;
    }
    
    struct Edge {
        int from;
        int to;
        T cap;
        T flow;
    };
    std::vector<Edge> edges() {
        std::vector<Edge> a;
        for (int i = 0; i < e.size(); i += 2) {
            Edge x;
            x.from = e[i + 1].to;
            x.to = e[i].to;
            x.cap = e[i].cap + e[i + 1].cap;
            x.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};




int n, m, tot, dfsn[N], ins[N], low[N];
stack<int> s;
vector<int> e[N];
vector<vector<int>> scc;
vector<int> b(N);

void dfs(int x)
{
	low[x] = dfsn[x] = ++tot, ins[x] = 1, s.push(x);
	for (auto u : e[x])
	{
		if (!dfsn[u])
		{
			dfs(u);
			low[x] = min(low[x], low[u]);
		}
		else if (ins[u])
			low[x] = min(low[x], dfsn[u]);
	}
	if (dfsn[x] == low[x])
	{
		vector<int> c;
		while (1)
		{
			auto t = s.top();
			c.push_back(t);
			ins[t] = 0;
			s.pop();
			b[t] = scc.size();
			// z[scc.size()]+=a[t];
			if (t == x)
				break;
		}
		scc.push_back(c);
	}
}

void add(int u, int v)
{
	e[u].push_back(v);
}

MaxFlow<int> mf;

int w[1005][1005];

void solve()
{
    cin>>n;
    // vector<int> st(30);
    string ss[n+5];
    for(int i=0;i<n;i++) cin>>ss[i];
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (ss[i].back() == ss[j].front())
                e[i].push_back(j);
        }
    }
    for(int i=0;i<n;i++){
        // if(!st[i]) continue;
        if(!dfsn[i]) dfs(i);
    }
    mf.init(n*n*2+5);
    int s=n*n+1,t=n*n+2;
    for(int i=0;i<n;i++){
        for(auto j: e[i]){
            if(b[i]!=b[j]){
                w[b[i]][b[j]]=1;
            }
        }
    }
    int res=scc.size();
    // cout<<res<<endl;
    for(int k=0;k<res;k++){
        for(int i=0;i<res;i++){
            for(int j=0;j<res;j++){
                if(i==j) continue;
                if(w[i][k]&&w[k][j]) w[i][j]=1;
            }
        }
    }
    for(int i=0;i<res;i++){
        for(int j=0;j<res;j++){
            if(w[i][j]&&i!=j) mf.addEdge(i,j+res,1);
        }
        mf.addEdge(s,i,1),mf.addEdge(i+res,t,1);
    }

    cout<<res-mf.flow(s,t)<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	// cin>>t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}
相关推荐
一只码代码的章鱼24 分钟前
排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)
数据结构·算法·排序算法
青い月の魔女43 分钟前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法
林的快手1 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
千天夜2 小时前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
从以前2 小时前
准备考试:解决大学入学考试问题
数据结构·python·算法
.Vcoistnt2 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
ALISHENGYA2 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论
我码玄黄5 小时前
正则表达式优化之算法和效率优化
前端·javascript·算法·正则表达式
Solitudefire5 小时前
蓝桥杯刷题——day9
算法·蓝桥杯
三万棵雪松6 小时前
1.系统学习-线性回归
算法·机器学习·回归·线性回归·监督学习