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
  • 相关阅读:
    20110228 12:20 .net httpHandler和httpModule
    Microsoft CRM 2011清除缓存
    Sql Server Reporing Service / Windows Azure SQL Reporting应用程序开发
    终于来Cnblogs开博啦!~
    瑞星与360事件依我之看
    xml 基础 学习
    程序员的人生
    C# 发送邮件 .net SendEmail 源码
    Repeater 嵌套 绑定数据,嵌套的Repeater无法绑定的问题
    从maya中 导入物体 到Uniyt3D 规范 整理
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12310271.html
Copyright © 2011-2022 走看看