美文网首页
差分约束,拓扑排序与SCC

差分约束,拓扑排序与SCC

作者: 大家好我是阿凉 | 来源:发表于2020-04-17 09:41 被阅读0次

A:区间选点——(差分约束与spfa)

题目:

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点

使用差分约束系统的解法解决这道题

输入:

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。

输出:

输出一个整数表示最少选取的点的个数

输入样例:

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

输出样例:

6
首先说啥是差分约束,大概就是
一种特殊的 n 元一次不等式组,它包含 n个变量以及m个约束条件。
• 每个约束条件是由两个其中的变量做差构成的,形如𝑥i−𝑥j≤𝑐k, 其中𝑐k是常数(可以是非负数,也可以是负数)。
• 我们要解决的问题是:求一组解𝑥1=𝑎1,𝑥2=𝑎2,…,𝑥n=𝑎n,使 得所有的约束条件得到满足,否则判断出无解。
• 注意到,如果{𝑎1,𝑎2,𝑎3,…,𝑎n}是该差分约束系统的一组解,那么对 于任意的常数𝑑,显然{𝑎1+𝑑,𝑎2+𝑑,…,𝑎n+𝑑}也是该差分约束系统一组解。
求解差分约束系统,都可以转化为图论中得单源最短路问题
对于差分约束中的每一个不等式约束Xi-Xj<Ck,都可以移项变形为Xi<=Ck+Xj.如果令Ck=w(i,j) dis[i]=Xi,dis[j]=Xj ,那么原式变为dis[i]<=dis[j]+w(i,j),与最短路中的松弛操作相似。
由于原式为Xi<=Ck+Xj,求解是按照等于求解,跑最短路出来的最大解,如果想要最小解即为Xi>=Ck+Xj,跑最长路。

构造不等式组:
记sum[i]表示数轴上[0,i]之间选点的个数
对于第i个区间[ai,bi]需要满足sum[bi]-sum[ai-1]>=ci
同时需要保证sum[i]是有意义的,
0<=sum[i]-sum[i-1]<=1;
这时就会出现0<=sum[1]-sum[0]<=1; 此时为了方便整体后移改成sum[bi+1]-sum[ai]>=ci ; (可以后移也是基于差分约束系统+d,-d结果不变)
(以上内容搬运自某学长的PPT orz 侵删)
以上搞明白以后,这题就像是一个板子题了,用SPFA求单源最短路

#include <iostream>
#include <queue>
#include<string>
#include<cstring>
#include<string.h>
using namespace std;
const int max1 = 1e8 * 5;
int m;
int head[1000000], dis[10000000], cnt[10001];
bool vis[100000];//, bl[10001];
//int al[1000000];
int max(int a, int b)
{
    if (a > b) return a;
    else return b;
}
struct edge
{
    int to;
    int next;
    int w;
};
int tot;
edge a[10000000];
void add(int x, int y, int w)
{
    a[++tot].to = y;
    a[tot].next = head[x];
    a[tot].w = w;
    head[x] = tot;
}
queue<int> q;
void SPFA()
{
    while (!q.empty()) q.pop();

    memset(vis, 0, sizeof(vis));
    for (int i = 0; i <= m; i++)
    {
        dis[i] = -max1;
    }
    dis[0] = 0;
    vis[0] = 1;
    q.push(0);
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        vis[t]=0;
        for (int i = head[t]; i; i = a[i].next)
        {
            int y = a[i].to, w = a[i].w;
            if (dis[y] < dis[t] + w)
            {
                dis[y] = dis[t] + w;
                if (!vis[y]) vis[y] = 1, q.push(y);
            }
        }
    }
}
int main()
{
    int n;
    int u, v, w;
    cin >> n;
    m = 0;
    memset(head, 0, sizeof(head));
    while(n--)
    {
        scanf("%d%d%d", &u, &v, &w);
        add(u, v + 1, w);
        m = max(m, v + 1);
    }
    for (int i = 1; i <= m; i++)
    {
        add(i, i - 1, -1);
        add(i - 1, i, 0); 
    }
    tot = 0;
        
    SPFA();

    cout << dis[m] << endl;
    return 0;
}

B:猫猫向前冲——(拓扑排序)

题目:

众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。

输入:

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。

输出:

给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

输入样例:

4 3
1 2
2 3
4 3

输出样例:

1 2 4 3

数据结构王老师举得拓扑排序的例子就是这个,拓扑排序本身思想比较简单。找一个入度为0的点。删去这个点和全部边。再找……
题目要求字典序最小。那正统的思路应该是用一个小根堆,每次把入度为零的全扔进去,取堆顶的点删去。
但我暴力居然过了。。。(没用小根堆优化)
我就放暴力的代码了(下次数据不一定善良,请优化)

#include<iostream>
#include<string.h>
using namespace std;
int mint(int a,int b)
{
    if(a>b) return b;
    else return a;
}
int head[1001]/*,vis1[1001]*/,vis[1001],dis1[1001],dis2[1001];
int father1[1001],father2[1001];
int min1(int a,int b)
{
    if(a>b) return b;
    else return a;
}
struct edge
{
    int to;
    int next;
};
int tot;
edge a[1000000];
void add(int x,int y)
{
    a[++tot].to=y;
    a[tot].next=head[x];
    head[x]=tot; 
}
int dil[1000000];
void quxiao(int t)
{
    for (int i = head[t]; i; i = a[i].next)
    {
        int y = a[i].to;
        dil[y]--;
    }
}
int main()
{
    int xulie[100000];
    
    int n,m;
    while(cin>>n)
    {
        memset(vis,0,sizeof(vis));
        memset(dil,0,sizeof(dil));
        memset(head,0,sizeof(head));
        cin>>m;
        for(int i=0;i<m;i++)
        {
            int u,v;
            cin>>u>>v;
            add(u,v);
            dil[v]++;
        }
        int w=0;
        int minc;
        while(w<n)
        {
            minc=1000000;
            for(int i=1;i<=n;i++)
            {
                if(!vis[i]&&dil[i]==0)
                {
                //  vis[i]=1;
                    minc=mint(minc,i);
                }
                
            }
            xulie[w]=minc;
            vis[minc]=1;
            quxiao(minc);
            w++;
        }
        for(int i=0;i<n;i++)
        {
            cout<<xulie[i];
            if(i!=n-1) cout<<" ";
        }
        cout<<endl;
    }
    return 0;
}

C:班长竞选——(SCC 与缩点)

题目:

大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

输入:

本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

输出:

对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

输入样例:

2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

输出样例:

Case 1: 2
0 1
Case 2: 2
0 1 2

这题就是,SCC+缩点
first,scc(找连通分量。)


image.png

(又不要脸的找来一堆资料,手打确实太麻烦)
其实就是两遍dfs的功夫。第一次确定(逆)后序序列,第二次把图返,然后确定每个点在哪个SCC里面。

第二部,缩点
缩点,每个SCC都作为一个点,连接SCC之间的线,得图G。此时存反图 fG。遍历点得到,每个 SCC中的详细的点。遍历边,然后寻找跨越不同SCC 中的边。
经分析可以知道,最大的票数应该在G 中指向SCC 最多的,但是这样计算不方便,因此用反图fG, 从出度为0的SCC 遍历,获取此SCC所能到达的所有SCC ,统计票数。

#include<iostream>
#include<vector>
#include <algorithm>
#include<string.h>
using namespace std;
const int N=5020;
int len;
int head1[5020],vis[5020];
vector<int> K1[5020],K2[5020],l[5020];
int ret[5020];
int num[5020];
int head2[5020];
struct edge
{
    int to;
    int next;
};
int tot1,tot2;
edge a[30010];
edge b[30010];
void add(int x,int y,edge *a,int *head,int &tot)
{
    a[++tot].to=y;
    a[tot].next=head[x];
    head[x]=tot; 
}
int n,f[N+1];
int dcnt=0,fcnt=0;
void dfs(int x,int *head)
{
    vis[x]=1;
    for (int i = head[x]; i; i = a[i].next)
    {
        int y=a[i].to;
        if(!vis[y])
        {
            dfs(y,head);
        }
    }
    f[x]=fcnt; fcnt++;
}
//int sccnum[5000];
int wk[5000+10];
int scc[5000+10];
int sccn=0;

void dfs2(int x,int &number)
{
    scc[x]=sccn;
    l[sccn].push_back(x);
    number++;
    vis[x]=1;
    for (int i = head2[x]; i; i = b[i].next)
    {
        int y=b[i].to;
        if(!vis[y])
        {
            dfs2(y,number);
        }
    }
}
void suodian()
{
    for(int x=0;x<n;x++)
    {
        for(int i=head1[x];i;i=a[i].next)
        {
            int y=a[i].to;
            if(scc[x]==scc[y])
                continue;
            K1[scc[x]].push_back(scc[y]);   
            K2[scc[y]].push_back(scc[x]);
        } 
    }
}

void dfs3(int x)
{
    vis[x]=1;
    len+=l[x].size();
    for(int i=0;i<K2[x].size();i++){
        if(!vis[K2[x][i]]){
            dfs3(K2[x][i]);
        }           
    }       
}

void solve(int *head)
{
    dcnt=0;fcnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++)
    {
        if(!vis[i]) dfs(i,head);
    }
}
void solve2()
{
    sccn=0;
    memset(vis,0,sizeof(vis));
    for(int i=0;i<n;i++)
    {
        int number=0;
        if(!vis[wk[i]]) dfs2(wk[i],number), sccn++;
        
    }
}
int main()
{
    int T;
    cin>>T;
    int w=1;
    while(T--)
    {
        tot1=0,tot2=0;
        //if(w!=1) cout<<endl;
        cout<<"Case "<<w<<": "; w++;
        cin>>n;
    
        memset(num,0,sizeof(num));
        memset(head1,0,sizeof(head1));
        memset(head2,0,sizeof(head2));
        int m;cin>>m;
        int u,v;
        while(m--)
        {
            cin>>u>>v;
            add(u,v,a,head1,tot1);
            add(v,u,b,head2,tot2);
        }
        solve(head1);
        for(int i=0;i<n;i++)
        {
            wk[n-1-f[i]]=i;
        }
    /*  for(int i=0;i<n;i++)
        {
            cout<<f[i]<<" ";
        }
        cout<<endl;
        for(int i=0;i<n;i++)
        {
            cout<<wk[i]<<" ";
        }*/
        solve2();
        suodian();
        int maxans=0;
        
        for(int i=0;i<sccn;i++)
        {
            if(K1[i].size()==0)
            {
                for(int j=0;j<=sccn;j++)
                    vis[j]=0;
                len=0;
                dfs3(i);
                num[i]=len-1;
                maxans=max(maxans,num[i]);
            }
        }   
        cout<<maxans<<endl;
        int dss=0;
        for(int i=0;i<sccn;i++)
        {
            if(num[i]==maxans)
            {
                for(int j=0;j<l[i].size();j++)
                {
                    ret[dss]=l[i][j];
                    dss++;
                }
            }
        }
    //  dss--;
        sort(ret,ret+dss);
        for(int kdd=0;kdd<dss;kdd++)
        {
            cout<<ret[kdd];
            if(kdd!=dss-1) cout<<" ";
        }
        cout<<endl;
    /*  for(int i=0;i<sccn;i++)
        {
            cout<<"n"<<num[i]<<endl;
        }
        for(int i=0;i<n;i++)
        {
            cout<<scc[i]<<" ss";
        }
        cout<<endl;*/
    
    /*  for(int i=0;i<n;i++)
        {
            if(scc[i]==dl)
            {
                cout<<i<<" ";
            }
        }*/
        
        /*cout<<endl;
        for(int i=0;i<n;i++)
        {
            cout<<f[i]<<" ";
        }
        cout<<endl;*/
        
        //cout<<"Case:"<<w<<" "; w++;
        for(int dssl=0;dssl<n;dssl++)
        {
            K1[dssl].clear();K2[dssl].clear();l[dssl].clear();
        }
    }
    return 0;
} 

相关文章

  • 差分约束,拓扑排序与SCC

    A:区间选点——(差分约束与spfa) 题目: 给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i ...

  • 差分约束

    差分约束 什么是差分约束? 差分约束系统(system of difference constraints),是求...

  • 差分约束

    差分约束 (1)求不等式组的可行解 原点需要满足的条件:从原点出发,一定可以走到所有的边。(这一点必须要满足,否则...

  • 利用DFS实现拓扑排序

    拓扑排序定义利用“DAG必有零入度顶点”的特性,实现拓扑排序基于DFS搜索的拓扑排序 1. 拓扑排序定义 将一个有...

  • 7.6图的应用:拓扑排序

    拓扑排序Topological Sort ❖从工作流程图得到工作次序排列的算法,称为“拓扑排序”❖拓扑排序处理一个...

  • 图的最短路径和拓扑排序

    拓扑排序 2、图的最短路径 3、图的拓扑排序

  • 拓扑排序(Topological Sorting)

    拓扑排序(Topological Sorting)拓扑排序(Topological Sorting)是一个有向无环...

  • 华东师范2019数据结构考题

    1,非递归二叉树遍历32分 2,拓扑排序环路判断32分

  • 判断有向图是否有环

    方法一:拓扑排序 时间复杂度O(n^2) 比较常用的是用拓扑排序来判断有向图中是否存在环。 什么是拓扑排序呢?我们...

  • 拓扑排序(二)——拓扑排序

    LeetCode_210_CourseScheduleII 解法分析: 解法:

网友评论

      本文标题:差分约束,拓扑排序与SCC

      本文链接:https://www.haomeiwen.com/subject/mygsvhtx.html