图论专题总结

T1菜肴制作:拓扑排序+大根堆

卡了好一会儿才过掉。正序拓扑的话贪心策略会出错。

保证先输出小的,倒序拓扑保证先搞大的。然后插到大根堆里。

每次取出最大的(堆顶)进行拓扑扩展。pop出来的直接扔进栈里。

多判有点恶心。记得清空(我就因为tot没清空,样例第三组单测正确,多测就错。。)

还有一个特殊判断:栈内元素个数与本身元素个数是否相符。

不相符就是剩下了环搞不出来,impossible就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#define rint register int
using namespace std;
int T,n,m,a,b,tot,first[100003];
int du[100003];
struct node{
    int u,v,nxt;
};
bool pan=0,vis[100003];
inline void add(int uu,int vv,node edge[])
{
    ++tot;
    edge[tot].u=uu;
    edge[tot].v=vv;
    edge[tot].nxt=first[uu];
    first[uu]=tot;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        memset(vis,0,sizeof(vis));
        memset(du,0,sizeof(du));
        memset(first,0,sizeof(first));
        tot=0;
        priority_queue <int> q;
        stack <int> s;
        node edge[100003];
        for(rint i=1;i<=m;++i)
        {
            scanf("%d %d",&a,&b);
            add(b,a,edge);du[a]++;
        }
        for(rint i=1;i<=n;++i)
        {
            if(!du[i])q.push(i);
            vis[i]=1;
        }
        if(q.empty()){cout<<"Impossible!"<<endl;continue;}
        while(!q.empty())
        {
            int xx=q.top();s.push(xx);q.pop();
//            cout<<xx<<endl;
            pan=0;
            for(rint i=first[xx];i;i=edge[i].nxt)
            {
                int yy=edge[i].v;
                du[yy]--;
                if(!du[yy])
                {
                    pan=1;
                    q.push(yy);
//                    cout<<"yy"<<yy<<endl;
                    vis[yy]=1;
                }
            }
//            if(pan==0&&q.empty()){cout<<"Impossible!"<<endl;break;}
        }
//        if(pan==0)continue;
        if(s.size()!=n){cout<<"Impossible!"<<endl;continue;}
        while(!s.empty())
        {
            cout<<s.top()<<‘ ‘;
            s.pop();
        }
        cout<<endl;
    }
}

T2矩阵游戏:二分图

锝瑟一把这题全hzoj我是第一个A掉的

二分图,一开始还真没想起来。查bzoj上这道题的时候撇了一眼看到了二分图三个字。

好的。(不要脸)但是真的忘了啊QAQ只好翻出ftp里尘封多年的二分图ppt。

手码了一遍匈牙利感觉还不错。

挺基础,行列分别建点,黑格建边,跑一边匈牙利,用黑格去匹配行和列。

然而没认真看题,人家让输出“Yes”或“No”,我输出“YES”“NO”完美w0。。。

加了一堆卡常特盘,148毫算是挺快的了吧?(wtf 102ms!收小的一拜)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=205;
int a[N][N];
int vis[N],matx[N],maty[N];
int tot;
int n;
int hun(int x)
{
    for(int i=1;i<=n;i++)
    {
        //int y=to[i];
        if(!vis[i]&&a[x][i])
        {
            vis[i]=1;
            if(maty[i]==-1||hun(maty[i]))
            {
                maty[i]=x;
                matx[x]=i;
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        tot=0;
        memset(maty,-1,sizeof(maty));
        memset(matx,-1,sizeof(matx));
        scanf("%d",&n);
        int cnt=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if(hun(i)) ans++;
        }
        //cout<<ans<<endl;
        if(ans!=n){
            printf("No\n");
        }
        else puts("Yes\n");
    }
}

T3约会 Rendezvous:基环树

为什么基环树的题目都这么恶心还是我太弱鸡了?

我可能卡了一年。自己想出来的代码5.0k,198行……我真的没勇气调下去了。

记录了一大堆东西。判断的语句写了一堆。。真的是堆QAQ。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#define rint register int
const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
using namespace std;
int n,m,head[500005],tot,fat[500005];
int dex,cnt,t,bar[500005],bl[500005],ac[500005];
int d[500005],f[500005][20],l_a[500005],l_b[500005];
vector<int>dell[500005];queue<int>q;
struct node{int to,nxt;}edge[500005];
int read()
{
    rint a=0,b=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)b=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){a=a*10+ch-‘0‘;ch=getchar();}
    return a*b;
}
void add(rint x,rint y)
{
    edge[++tot].to=y;
    edge[tot].nxt=head[x];
    head[x]=tot;
}
int find(rint x)
{
    if(fat[x]==x) return x;
    return fat[x]=find(fat[x]);
}
void get_dell(rint x,rint dex)
{
    bar[x]=dex;
    for(rint i=head[x];i;i=edge[i].nxt)
    {
        rint y=edge[i].to;
        if(bar[y]==dex)
        {
            ++cnt;
            while(y!=x)
            {
                dell[cnt].push_back(x);
                x=ac[x];
            }
            dell[cnt].push_back(y);
        }
        else ac[y]=x,get_dell(y,dex);
    }
}
void bfs(rint x,rint tp)
{
    d[x]=1,bl[x]=tp;
    q.push(x);
    while(!q.empty())
    {
        rint x=q.front();
        q.pop();
        for(rint i=head[x];i;i=edge[i].nxt)
        {
            rint y=edge[i].to;
            if(!bar[y]&&!d[y])
            {
                bl[y]=tp;d[y]=d[x]+1;
                q.push(y);f[y][0]=x;
                for(rint j=1;j<=t;j++)
                    f[y][j]=f[f[y][j-1]][j-1];
            }
        }
    }
}
int Lca(rint x,rint y)
{
    if(d[x]>d[y]) swap(x,y);
    for(rint i=t;i>=0;i--)
        if(d[f[y][i]]>=d[x]) y=f[y][i];
    if(x==y) return x;
    for(rint i=t;i>=0;i--)
    {
        if(f[y][i]^f[x][i])
        {
            y=f[y][i];
            x=f[x][i];
        }
    }
    return f[x][0];
}
void solve()
{
    for(rint i=1;i<=n;i++)
        if(!bar[i]) get_dell(i,++dex);
    memset(bar,0,sizeof(bar));
    for(rint i=1;i<=cnt;i++)
        for(rint j=0;j<dell[i].size();j++)
            bar[dell[i][j]]=1,l_a[dell[i][j]]=j,l_b[dell[i][j]]=i;
    for(rint i=1;i<=cnt;i++)
        for(rint j=0;j<dell[i].size();j++)
            bfs(dell[i][j],dell[i][j]);
}
int main()
{
    n=read(),m=read();
    t=log2(n)+1;
    for(rint i=1;i<=n;i++) fat[i]=i;
    for(rint i=1,x;i<=n;i++)
    {
        x=read();
        add(x,i);
        fat[find(i)]=find(x);
    }
    solve();
    for(rint i=1,x,y;i<=m;i++)
    {
        x=read(),y=read();
        if(find(x)!=find(y)) printf("-1 -1\n");
        else if(bl[x]==bl[y])
        {
            rint lca=Lca(x,y);
            rint ll=d[x]-d[lca],rr=d[y]-d[lca];
            printf("%d %d\n",ll,rr);
        }
        else
        {
            rint ll=abs(l_a[bl[x]]-l_a[bl[y]]),rr=dell[l_b[bl[x]]].size()-ll;
            rint edge=d[x]-d[bl[x]],r=d[y]-d[bl[y]];
            rint ans1,ans2,ans3,ans4;
            if(l_a[bl[x]]<l_a[bl[y]]) ans1=edge+ll,ans2=r,ans3=edge,ans4=r+rr;
            else ans1=edge+rr,ans2=r,ans3=edge,ans4=r+ll;
            if(max(ans1,ans2)<max(ans3,ans4)) printf("%d %d\n",ans1,ans2);
            else if(max(ans1,ans2)>max(ans3,ans4)) printf("%d %d\n",ans3,ans4);
            else
            {
                if(min(ans1,ans2)<min(ans3,ans4)) printf("%d %d\n",ans1,ans2);
                else if(min(ans1,ans2)>min(ans3,ans4)) printf("%d %d\n",ans3,ans4);
                else printf("%d %d\n",max(ans1,ans2),min(ans1,ans2));
            }
        }
    }
    return 0;
}

T4tree:最小生成树+二分答案

被天皇忽悠说这道题贼恶心,去看题解(推锅推锅)然后1A掉不想说话。

不是特别好想。不过填了一种最小生成树题目的做题方法:通过改变边权来改变最小生成树的构成。

黑白边,要求need条白边。一开始sb的想成了树里只有白边。

显然还有n-1-need条黑边 /糊脸(n是节点数目)顺嘴吐槽:BZOJ上直接跑最小生成树都能过……

二分给白边加上边权,跑kruskal最小生成树,看是否符合need条白边。

直接暴力累计就好了没必要开优化(懒)还有人直接爆搜不二分都A了……

   <-这是爆搜

<-这是二分

建议打二分咕咕咕~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define rint register int
using namespace std;
int n,m,need,l,r,cnt,tot,ans;
int u[100005],v[100005],c[100005],col[100005];
int fa[100005];
struct node {int u,v,c,col;}edge[100005];
inline int read()
{
    int a=0,b=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)b=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){a=(a<<3)+(a<<1)+(ch-‘0‘);ch=getchar();}
    return a*b;
}
inline bool cmp(node a,node b){return a.c==b.c?a.col<b.col:a.c<b.c;}
inline int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
inline bool kruskal(int x)
{
    int f1,f2,sum=0;
    for(rint i=1;i<=n;i++) fa[i]=i;
    for(rint i=1;i<=m;i++)
    {
        edge[i].u=u[i];
        edge[i].v=v[i];
        edge[i].c=c[i];
        edge[i].col=col[i];
        if(!col[i]) edge[i].c+=x;
    }
    sort(edge+1,edge+m+1,cmp);
    for(rint i=1;i<=m;i++)
    {
        f1=get(edge[i].u),f2=get(edge[i].v);
        if(f1!=f2)
        {
            fa[f1]=f2;
            sum++;
            tot+=edge[i].c;
            if(!edge[i].col) cnt++;
        }
        if(sum==n-1)
            if(cnt>=need)return 1;
            else return 0;
    }
}
int main()
{
    n=read(),m=read(),need=read();
    for(rint i=1;i<=m;i++)
    u[i]=read()+1,v[i]=read()+1,c[i]=read(),col[i]=read();
    l=-101,r=101;
    while(l<r)
    {
        tot=cnt=0;
        int mid=(l+r)>>1;
        if(kruskal(mid))
            l=mid+1,ans=tot-need*mid;
        else r=mid;
    }
    cout<<ans<<endl;
    return 0;
}

T5太鼓达人:dfs欧拉回路

颓了题解,膜拜soul神佬。

一看题真的想不出来哪里和图论有关系……查了一下全说打表……

原文地址:https://www.cnblogs.com/xingmi-weiyouni/p/11180135.html

时间: 2024-08-30 02:10:08

图论专题总结的相关文章

UVa10048_Audiophobia(最短路/floyd)(小白书图论专题)

解题报告 题意: 求所有路中最大分贝最小的路. 思路: 类似floyd算法的思想,u->v可以有另外一点k,通过u->k->v来走,拿u->k和k->v的最大值和u->v比较,存下最小的值. #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #define inf 0x3f3f3f3f using namespace std;

UVa10397_Connect the Campus(最小生成树)(小白书图论专题)

解题报告 题目传送门 题意: 使得学校网络互通的最小花费,一些楼的线路已经有了. 思路: 存在的线路当然全都利用那样花费肯定最小,把存在的线路当成花费0,求最小生成树 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #define inf 0x3f3f3f3f using namespace std; int n,m,_hash[1110][1110],vis

UVa10099_The Tourist Guide(最短路/floyd)(小白书图论专题)

解题报告 题意: 有一个旅游团现在去出游玩,现在有n个城市,m条路.由于每一条路上面规定了最多能够通过的人数,现在想问这个旅游团人数已知的情况下最少需要运送几趟 思路: 求出发点到终点所有路当中最小值最大的那一条路. 求发可能有多种,最短路的松弛方式改掉是一种,最小生成树的解法也是一种(ps,prime和dijs就是这样子类似的) #include <iostream> #include <cstdio> #include <cstring> #include <

UVa10034/POJ2560_Freckles(最小生成树)(小白书图论专题)

解题报告 题意: 把所有点连起来,求使用的墨水最少. 思路: 裸最小生成树. #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define inf 0x3f3f3f3f using namespace std; struct N { double x,y; } node[110]; int vis[110],n; double mmap[110][110],

正睿OI国庆DAY2:图论专题

正睿OI国庆DAY2:图论专题 dfs/例题 判断无向图之间是否存在至少三条点不相交的简单路径 一个想法是最大流(后来说可以做,但是是多项式时间做法 旁边GavinZheng神仙在谈最小生成树 陈主力说做法是dfs 首先两个点一定在点双联通分量里 点双是简单环,只有两条,不存在 猜测其他情况存在三条 双联通分量分解 输出情况可以用dfs树判,讨论非树边覆盖情况 内包含 下面分叉连到上面 相交 输出点即可 BFS/例题 BFS树没有跳跃边 计数/动态规划有用吧 树上bfs序好像可以判断距离? 边权

【解题报告】【图论专题】

图论500题http://blog.csdn.net/luomingjun12315/article/details/47438607 [最小生成树+并查集] 1.hdu 1856  基础并查集★ /* 转化为图论模型 求一个图的最大联通分量 用并查集,每个联通分量用一个集合维护,最后求集合元素个数的最大值 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; cons

UVa10803_Thunder Mountain(最短路)(小白书图论专题)

解题报告 裸floyd. #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define inf 0x3f3f3f3f using namespace std; int n; double mmap[210][210]; struct node { double x,y; } p[210]; double disc(node p1,node p2) { ret

UVa558_Wormholes(最短路)(小白书图论专题)

解题报告 思路: spfa判负环. #include <iostream> #include <cstring> #include <cstdio> #include <queue> #define inf 0x3f3f3f3f #define N 40000 #define M 100000 using namespace std; struct node { int v,w,next; } edge[M]; int head[N],dis[N],vis[

UVa10806_Dijkstra, Dijkstra.(网络流/费用流)(小白书图论专题)

解题报告 思路: 从s->t 再从t->s等同与s->t两次,要求每条路只能走一次,要求最小花费,让每一条边容量为1,跑跑费用流 只要跑出流量为2就结束. #include <iostream> #include <cstring> #include <cstdio> #include <queue> #define inf 0x3f3f3f3f #define N 5000 #define M 50000 using namespace