zoukankan      html  css  js  c++  java
  • 洛谷专题-较复杂图论I

    P1268 树的重量 (思维题)

    题意:

    首先定义树的重量为数上每个边权的和,现在给你每个叶子结点之间的最短距离,让你求出树的重量

    思路:

    先定义叶子节点之间的距离为dis[u,v]

    那么我们会先考虑只有两个节点的情况,那么答案就为dis[u,v]

    当n=3,我们考虑将3号节点加入树中,由于每个节点都是叶子节点,那么3号节点只能由一条边连接至1与2的连线上

    长度len = (dis(1,3)+dis(2,3)-dis(1,2))/2

    n>3的情况也同理。枚举i,看看点n是不是从点1~i的路径上分叉出来的,求出的最小len就是要加到答案里面去的

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define inf 0x3f3f3f3f
     using namespace std;
     const int maxn=35;
     int dis[maxn][maxn];
     int main()
     {
         int n,x;
         while(scanf("%d",&n)&&n){
             memset(dis,0,sizeof(dis));
             for(int i=1;i<n;++i){
                 for(int j=i+1;j<=n;++j){
                     scanf("%d",&x);
                     dis[i][j]=x,dis[j][i]=x;
                 }
             }
            int ans=dis[1][2];
            for(int i=3;i<=n;i++){
                int len=inf;
                for(int j=1;j<i;j++){
                    for(int k=1;k<j;k++)
                        len=min(len,(dis[i][j]+dis[k][i]-dis[k][j])>>1);
                }
                ans+=len;
            }
            cout<<ans<<endl;
         }
     }
    View Code

    P1113 杂务(拓扑排序)

    题意:

    有n个任务,每个任务完成都有其所需的时间,并且有其前置任务,问完成所有任务要多久(没有直接关系的任务可以同时开始)

    思路:

    自己写的稍稍有些复杂

    首先建图,对于一个任务给所有其前置任务连一条有向边(从前置连向自己),并记录一个点的入读

    之前遍历一遍,将入度为0的点加入队列中,边将其所有相邻的点入度减一,如果有入度为0的点,则加入队列中

    比较关键的一点就是如何更新时间dp[nex]=max(dp[nex],dp[i]+tim[nex])

    我的写法较为复杂,还多用了一个队列存时间,用一个DP数组就能直接解决了

    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #define inf 0x3f3f3f3f
     using namespace std;
     const int maxn=1e5+10;
     queue<int> a,b;
     vector<int> edge[maxn];
     int in[maxn],tim[maxn],ans,n,mx[maxn];
     void solve()
     {
         for(int i=1;i<=n;i++){
             if(!in[i]){
                 a.push(i),b.push(tim[i]);
             }
         }
         while(!a.empty()){
             int x=a.front(),y=b.front();
             a.pop(),b.pop();
             ans=max(ans,y);
             for(int i=0;i<edge[x].size();i++){
                 in[edge[x][i]]--;
                 mx[edge[x][i]]=max(y,mx[edge[x][i]]);
                 if(!in[edge[x][i]]){
                     a.push(edge[x][i]);
                     b.push(tim[edge[x][i]]+mx[edge[x][i]]);
                 }
             }
         }
     }
     int main()
     {
         int x,y,temp;
         scanf("%d",&n);
         for(int i=1;i<=n;i++){
             scanf("%d%d",&x,&y);
             tim[x]=y;
             while(scanf("%d",&temp)&&temp){
                 edge[temp].push_back(x);
                 in[x]++;
             }
         }
         memset(mx,-inf,sizeof(mx));
         ans=-inf;
         solve();
         cout<<ans<<endl;
     }
    View Code

    P1525 关押罪犯(二分图/并查集)

    题意:

    给你m对矛盾关系,每对关系分别涉及到x,y两人,矛盾值为w

    请你判断分配x和y到两个集合中,能否避免冲突

    如能避免请输出0,如果冲突不可避免,请输出最小的矛盾值

    思路:

    方法①:并查集

    并查集能维护连通性、传递性,通俗地说,亲戚的亲戚是亲戚

    我们不妨这样想:两个人a,b有仇,那么把他们放在一起显然会打起来,那么我们还不如把a与b的其他敌人放在一起,

    因为这样可能会出现“敌人的敌人就是朋友”的情况,恰好a与b的其他敌人之间没有矛盾,那么他们就可以放在同一个集合中,反之b对a亦然

    我们可以将没对关系按照权值从大到小排序,这样可以保证一旦发生冲突,答案是最小的

    对于加入的每段关系,我们先判断他们是否在同一集合内,如果在的话就说明发生冲突,直接输出答案

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
     using namespace std;
     const int maxn1=2e4+10;
     const int maxn2=1e5+10;
     int fa[maxn1],ene[maxn1];
     struct node{
         int u,v,w;
     }edge[maxn2];
     int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
     int cmp(node a,node b){return a.w>b.w;}
     int main()
     {
         int n,m;
         scanf("%d%d",&n,&m);
         for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
         for(int i=1;i<=n;i++) fa[i]=i;
         memset(ene,0,sizeof(ene));
         sort(edge+1,edge+1+m,cmp);
         for(int i=1;i<=m;i++){
             int u=edge[i].u,v=edge[i].v,w=edge[i].w;
             int f1=find(u),f2=find(v);
             if(f1==f2){
                 cout<<w<<endl;
                 return 0;
             }
            else{
                if(!ene[u])    ene[u]=v;
                else    fa[find(ene[u])]=find(v);
                if(!ene[v])    ene[v]=u;
                else    fa[find(ene[v])]=find(u);
            }
         }
        cout<<0<<endl;
        return 0;
     }
    View Code

    方法②:二分图

    看到将犯人分成两批应该很容易想到二分图的做法

    那么很明显,不是所有所有情况下都能讲犯人分成两部分,必定有一些冲突是无法避免的

    我们也可以注意到,冲突是具有单调性的,我们就可以想到二分的做法

    我们二分答案,设当前二分的值为mid,此时任意两个矛盾双方x和y必须被分在两个不同集合中,将罪犯们作为节点,在矛盾值大于等于mid的罪犯之间连一条边,我们得到一张无向图。此时我们只需判定这张无向图是否为二分图即可(因为要分为两部分),如果是二分图,令二分右端点R=mid,否则令L=mid即可

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<cstdio>
     using namespace std;
     const int maxn=2e5+10;
     struct edge{
         int u,v,w;
     }p[maxn];
     int n,m,L=0,R=0,cnt=0,head[maxn];
     void add_edge(int x,int y,int w)
     {
         p[++cnt].u=head[x];
         head[x]=cnt;
         p[cnt].v=y;
         p[cnt].w=w;
     }
     bool judge(int mid)
     {
         queue<int> q;
         int color[maxn]={0};
         for(int i=1;i<=n;i++){
             if(!color[i]) color[i]=1,q.push(i);
             while(!q.empty()){
                 int x=q.front();
                 q.pop();
                 for(int j=head[x];j;j=p[j].u){
                     if(p[j].w>=mid){
                         if(!color[p[j].v]){
                             q.push(p[j].v);
                             if(color[x]==1)    color[p[j].v]=2;
                             else    color[p[j].v]=1;
                         }
                        else if(color[p[j].v]==color[x])
                            return false;
                     }
                 }
             }
         }
         return true;
     }
     int main()
     {
         scanf("%d%d",&n,&m);
         for(int i=1;i<=m;i++){
             int u,v,w;
             scanf("%d%d%d",&u,&v,&w);
             R=max(R,w);
             add_edge(u,v,w);
             add_edge(v,u,w);
         }
        int ans=0;
        R++;
        while(L+1<R){
            int mid=(L+R)>>1;
            if(judge(mid))    R=mid;
            else L=mid;
        }
        cout<<L<<endl;
        return 0;
      } 
    View Code
  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12310271.html
Copyright © 2011-2022 走看看