luogu解题报告:P1262间谍网络【代码量惊人】【图论/强连通缩点】

题目见https://www.luogu.org/problem/show?pid=1262

分析

这个题一眼看上去并不难,似乎bfs就能解决问题,但事实上bfs只能解决第一问。第二问如何解决呢?首先要明确:强连通分量内如果有一个间谍被捕,所有人就会被捕,代价是其中所需金额最少的一个。因此可以进行强连通缩点,变成一个DAG。不难发现,金额最少就是DAG中所有入度为0的点的金额和。因为这些点非取不可,且取了就可以控制所有人。
然后就开始考验码农功了。。。。。对象的思想得到了运用。。

示例代码

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

//#define DEBUG

/*basis of graph*/
class graph {
public:
        struct p {
                int to, next;
        } edge[9005];
        int head[3003], top;
        int rank[3003];
        int rd[3003];
        graph() 
        {
                memset(head, 0, sizeof head);
                memset(rd, 0, sizeof rd);
                memset(rank, 127/3, sizeof rank);
                top = 0;
        }
        void edit_rank(int i, int j)
        {
                rank[i] = j;
        }
        void push(int i, int j)
        {
                edge[++top].to = j;
                edge[top].next = head[i];
                head[i] = top;
                rd[j]++;
        }
};

/* vars */
graph basic, reversed, solved;
int n, p, r;
int grp = 0;

/*kosaraju algorithm*/
int dfn[3005], t = 0, visited[3005], rank_g[3005];
int hash_tab[3005][3005];
void get_dfn(int i)
{
        if (!visited[i]) {
                visited[i] = 1;
                for (int k = basic.head[i]; k; k = basic.edge[k].next) 
                        get_dfn(basic.edge[k].to);
                dfn[++t] = i;
        }
}
void dfs(int i, int group)
{
        #ifdef DEBUG
        cout << "DEBUG DFS CALLED i = " << i << " ; group = " << group << endl;
        #endif
        visited[i] = group;
        rank_g[group] = min(rank_g[group], basic.rank[i]);
        for (int k = reversed.head[i]; k; k = reversed.edge[k].next)
                if (!visited[reversed.edge[k].to])
                        dfs(reversed.edge[k].to, group);
}

/*work functions*/

// using by function::able & funcion::work
queue<int> que;
bool killed[3005];

bool able()
{
        int count = 0;
        for (int i = 1; i <= n; i++)
                if (basic.rank[i] <= 233333333) {
                        que.push(i);
                        killed[i] = 1;
                }
        while (!que.empty()) {
                int i = que.front(); 
                que.pop();
                count++;
                for (int k = basic.head[i]; k; k = basic.edge[k].next) {
                        int to = basic.edge[k].to;
                        if (!killed[to]) {
                                killed[to] = 1;
                                que.push(to);
                        }
                }
        }
        return count == n;
}

void view(const graph &g)
{
        cout << "VIEWING GRAPH......" << endl;
        int k;
        cin >> k;
        for (int i = 1; i <= n; i++) {
                if (g.head[i])
                        cout << "." << i << endl;
                for (int k = g.head[i]; k; k = g.edge[i].next)
                        cout << "--\t" << g.edge[k].to << endl;
        }
        cout << "Finish." << endl;
}

void work()
{
        #ifdef DEBUG
        view(basic);
        view(solved);
        #endif
        if (!able()) {
                cout << "NO" << endl;
                for (int i = 1; i <= n; i++)
                        if (!killed[i]) {
                                cout << i << endl;
                                return;
                        }
        } 
        int ans = 0;
        for (int i = 1; i <= grp; i++)
                if (solved.rd[i] == 0) {
                        #ifdef DEBUG
                        cout << i << endl;
                        #endif
                        ans += rank_g[i];
                }
        cout << "YES" << endl << ans << endl;
}

/*main function*/
int main() {
        memset(visited, 0, sizeof visited);
        memset(rank_g, 127/3, sizeof rank_g);
        ios::sync_with_stdio(false);
        cin >> n;
        cin >> p;
        for (int i = 1; i <= p; i++) {
                int a, b;
                cin >> a >> b;
                basic.edit_rank(a, b);
        }
        cin >> r;
        for (int i = 1; i <= r; i++) {
                int a, b;
                cin >> a >> b;
                basic.push(a, b);
                reversed.push(b, a);
        }
        /* main process for strong connected graph*/
        for (int i = 1; i <= n; i++)
                get_dfn(i);
        memset(visited, 0, sizeof visited);
        for (int i = t; i; i--)
                if (!visited[dfn[i]]) 
                        dfs(dfn[i], ++grp);
        #ifdef DEBUG
        for (int i = 1; i <= n; i++)
                cout << visited[i] << " ";
        cout << endl;        
        #endif
        memset(hash_tab, 0, sizeof hash_tab);
        for (int i = 1; i <= n; i++)
                for (int k = basic.head[i]; k; k = basic.edge[k].next) {
                        int to = basic.edge[k].to;
                        if (visited[to] != visited[i] && !hash_tab[visited[i]][visited[to]]) {
                                solved.push(visited[i], visited[to]);
                                hash_tab[visited[i]][visited[to]] = 1;
                        }
                }
        /* main work */
        work();
        return 0;
}

注:代码用vscode完成,果然优美2333.

转载于:https://www.cnblogs.com/ljt12138/p/6684364.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值