zoukankan      html  css  js  c++  java
  • 7.8练习赛

    7.8练习赛

    A修公路
    时间限制 : - MS   空间限制 : 65536 KB 
    评测说明 : 时限1000ms

     

    问题描述

    某岛国有n个小岛构成(编号1到n),该国政府想要通过n-1条双向公路将这些小岛连接起来,使得任意两个岛屿之间都能相互到达。公路有两种,一种是高速公路,车速快,建设花费大;另一种是普通公路,车速慢,建设花费少。该国政府不想在一条公路上花费过多的钱,但又要求修建的公路中至少有k条高速公路。所以政府希望,在满足上述条件的情况下,使得最贵的一条公路花费尽可能少,请你帮忙计算出其中最贵一条公路的价格。

    输入格式

    第一行,三空格间隔的整数n,k,m,其中m表示有m对岛屿间可以修路。
         接下来以下的m行,每行四个正整数a,b,c1,c2 表示在岛屿a与b 之间修高速公路花费c1块钱,修普通公路,花费c2块钱。

    输出格式

    一个整数表示最贵那条公路的费用。

    样例输入 1

    4 2 4
    1 2 6 5
    1 3 3 1
    2 4 6 1
    3 4 4 2

    样例输出 1

    4

    样例输入 2

    4 1 5
    1 2 6 5
    1 3 3 1
    2 3 9 4
    2 4 6 1
    3 4 4 3

    样例输出 2

    3

    样例输入 3

    10 4 19
    3 9 6 3
    1 3 4 1
    5 3 10 2
    8 9 8 7
    6 8 8 3
    7 1 3 2
    4 9 9 5
    10 8 9 1
    2 6 9 1
    6 7 9 8
    2 6 2 1
    3 8 9 5
    3 2 9 6
    1 6 10 3
    5 6 3 1
    2 7 6 1
    7 8 6 2
    10 9 2 1
    7 1 10 2

    样例输出 3

    5

    提示

    对于30%的数据, 1<=n<=1000           0<=k<=10             n-1<=m<=3000

    对于100%的数据,1<=n<=10000          0<=k<=n-1            n-1<=m<=20000

    二分答案+最少生成树思想
    二分生成树的最大边权mid,判断这样的生成树是否存在就行了

    每次判断时分成两步走,首先讨论高速公路,选用的高速公路边权小于等于mid,能加的边都加进去
    判断生成树中的树边个数是否小于等于k,若小于k,表明这个生成树不存在。

    再讨论普通公路,能加进去的边都加进去,加完边后判断生成树的树边是不是n-1
    (生成树联通了所有的点),若不是,表明这个生成树不存在。

    老板代码

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 inline int read()
     6 {
     7     int x=0;char ch=getchar();
     8     while(ch<'0'||ch>'9')ch=getchar();
     9     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    10     return x;
    11 }
    12 int n,K,m,ans;
    13 int fa[10005];
    14 struct data{int x,y,c1,c2;}e[20005];
    15 int find(int x)
    16 {return x==fa[x]?x:fa[x]=find(fa[x]);}
    17 bool jud(int x)
    18 {
    19     for(int i=1;i<=n;i++)fa[i]=i;
    20     int cnt=0;
    21     for(int i=1;i<=m;i++)
    22     {
    23         if(e[i].c1>x)continue;
    24         int p=find(e[i].x),q=find(e[i].y);
    25         if(p!=q)
    26         {fa[p]=q;cnt++;}
    27     }
    28     if(cnt<K)return 0;
    29     for(int i=1;i<=m;i++)
    30     {
    31         if(e[i].c2>x)continue;
    32         int p=find(e[i].x),q=find(e[i].y);
    33         if(p!=q)
    34         {fa[p]=q;cnt++;}
    35     }
    36     if(cnt!=n-1)return 0;
    37     return 1;
    38 }
    39 int main()
    40 {
    41     freopen("road.in","r",stdin);
    42     freopen("road.out","w",stdout);
    43     n=read(),K=read(),m=read();
    44     for(int i=1;i<=m;i++)
    45         e[i].x=read(),e[i].y=read(),e[i].c1=read(),e[i].c2=read();
    46     int l=1,r=30000;
    47     while(l<=r)
    48     {
    49         int mid=(l+r)>>1;
    50         if(jud(mid)){ans=mid;r=mid-1;}
    51         else {l=mid+1;}
    52     }
    53     printf("%d",ans);
    54     return 0;
    55 }

    自己的:

     1 #include<stdio.h>
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 int father[10005];
     5 struct ztcakioi
     6 {
     7     int a,b,gao,pu;
     8 };
     9 int n,k,m,L=1,R=2000005,mid;
    10 ztcakioi edge[20005];
    11 int read()
    12 {
    13     int x=0;
    14     char c=getchar();
    15     while(c<'0'||c>'9')c=getchar();
    16     while(c>='0'&&c<='9')
    17     {
    18         x=(x<<1)+(x<<3)+c-'0';
    19         c=getchar();
    20     }
    21     return x;
    22 }
    23 int getfather(int p)
    24 {
    25     if(father[p]!=p)father[p]=getfather(father[p]);
    26     return father[p];
    27 }
    28 bool Kruskal(int ans)
    29 {
    30     int i,x,y,cnt=0;
    31     for(int i=1; i<=n; i++)father[i]=i;
    32     for(int i=1; i<=m&&cnt<n-1; i++)
    33     {
    34         if(edge[i].gao>ans)continue; //超过ztcakioi二分结果,返回 
    35         x=edge[i].a;
    36         y=edge[i].b;
    37         x=getfather(x);
    38         y=getfather(y);
    39         if(x==y)continue;
    40         father[x]=y;
    41         cnt++;//ztcakioi 次数+1 
    42     }
    43     if(cnt<k)return false;//ztcakioi次数不合条要求 ,返回 
    44     for(int i=1; i<=m&&cnt<n-1; i++)
    45     {
    46         if(edge[i].pu>ans)continue;//超过ztcakioi二分结果,返回 
    47         x=edge[i].a;
    48         y=edge[i].b;
    49         x=getfather(x);
    50         y=getfather(y);
    51         if(x==y)continue;
    52         father[x]=y;
    53         cnt++;
    54     }
    55     if(cnt==n-1)return true;//ztcak了全部赛事 
    56     return false;
    57 }
    58 int main()
    59 {
    60     ios::sync_with_stdio(false);
    61     cout.tie(NULL);
    62     n=read(),k=read(),m=read();
    63     for(int i=1; i<=m; i++)
    64     {
    65         edge[i].a=read(),edge[i].b=read(),edge[i].gao=read(),edge[i].pu=read();
    66     }
    67     while(L<=R)//二分
    68     {
    69         mid=(L+R)/2;
    70         if(Kruskal(mid))R=mid-1;
    71         else L=mid+1;
    72     }
    73     cout<<L;
    74 }
    B【USACO 2015 Jan Gold】牧草鉴赏家
    时间限制 : 10000 MS   空间限制 : 65536 KB

    问题描述

    约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

    贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

    输入格式

    第一行,两个整数N和M(1<=N,M<=100000)
    接下来M行,表示有M条单向道路,每条道路有连个整数X和Y表示,从X出发到达Y。

    输出格式

    一个整数,表示所求答案

    样例输入

    7 10
    1 2
    3 1
    2 5
    2 4
    3 7
    3 5
    3 6
    6 5
    7 2
    4 7

    样例输出

    6

    提示

    贝西的行走线路是1, 2, 4, 7, 2, 5, 3, 1 ,在5到3的时候逆行了一次。

    题意:

    给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,
    问最多有多少个点可以被经过?(一个点在路径中无论出现多少次(≥1)对答案的贡献均为1)

    题解:

    首先强连通分量缩点。
    然后形成了dfs统计出:
    集合A:点 1 能到哪些点,
    集合B:哪些点能到点 1
    然后这两个集合各为拓扑图。
    现在一条从1出发,最后又回到1的最长路径就可以被表示为
    1~A中某点a最长链+此点通过逆向边直接回到集合B中某点b后1~b的最长链。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<algorithm>
      4 #include<cstring>
      5 #include<cmath>
      6 #include<queue>
      7 #define N 200003
      8 using namespace std;
      9 int n,m;
     10 int point[N],v[N],Next[N],x[N],y[N],dis[N],dis1[N],can[N];
     11 int belong[N],size[N],st[N],top,tot,cnt,sz,dfsn[N],ins[N],low[N];
     12 void add(int x,int y)
     13 {
     14     tot++;
     15     Next[tot]=point[x];
     16     point[x]=tot;
     17     v[tot]=y;
     18 
     19 }
     20 void tarjan(int x)
     21 {
     22     dfsn[x]=low[x]=++sz;
     23     st[++top]=x;
     24     ins[x]=1;
     25     for (int i=point[x]; i; i=Next[i])
     26         if (!dfsn[v[i]])
     27         {
     28             tarjan(v[i]);
     29             low[x]=min(low[x],low[v[i]]);
     30         }
     31         else if (ins[v[i]])  low[x]=min(low[x],dfsn[v[i]]);
     32     if (dfsn[x]==low[x])
     33     {
     34         ++cnt;
     35         int j;
     36         do
     37         {
     38             j=st[top--];
     39             belong[j]=cnt;
     40             size[cnt]++;
     41             ins[j]=0;
     42         }
     43         while (j!=x);
     44     }
     45 }
     46 void spfa(int dis[N])
     47 {
     48     memset(dis,0,sizeof(dis));
     49     memset(can,0,sizeof(can));
     50     int t=belong[1];
     51     dis[t]=size[t];
     52     can[t]=1;
     53     queue<int> p;
     54     p.push(t);
     55     while (!p.empty())
     56     {
     57         int now=p.front();
     58         p.pop();
     59         for (int i=point[now]; i; i=Next[i])
     60             if (dis[v[i]]<dis[now]+size[v[i]])
     61             {
     62                 dis[v[i]]=dis[now]+size[v[i]];
     63                 if (!can[v[i]])
     64                 {
     65                     can[v[i]]=1;
     66                     p.push(v[i]);
     67                 }
     68             }
     69         can[now]=0;
     70     }
     71 }
     72 int main()
     73 {
     74 
     75     scanf("%d%d",&n,&m);
     76     for (int i=1; i<=m; i++)
     77     {
     78         scanf("%d%d",&x[i],&y[i]);
     79         add(x[i],y[i]);
     80     }
     81     for (int i=1; i<=n; i++)
     82         if (!dfsn[i]) tarjan(i);
     83 
     84     tot=0;
     85     memset(point,0,sizeof(point));
     86     memset(Next,0,sizeof(Next));
     87     for (int i=1; i<=m; i++)
     88         if (belong[x[i]]!=belong[y[i]])   add(belong[x[i]],belong[y[i]]);
     89     spfa(dis);
     90     //cout<<endl;
     91     tot=0;
     92     memset(point,0,sizeof(point));
     93     memset(Next,0,sizeof(Next));
     94     for (int i=1; i<=m; i++)
     95         if (belong[x[i]]!=belong[y[i]])   add(belong[y[i]],belong[x[i]]);
     96     spfa(dis1);
     97     int ans=size[belong[1]];
     98 
     99     for (int i=1; i<=m; i++)
    100         if (belong[x[i]]!=belong[y[i]])
    101         {
    102             int t=belong[x[i]];
    103             int t1=belong[y[i]];
    104             if (dis[t1]&&dis1[t]) ans=max(ans,dis[t1]+dis1[t]-size[belong[1]]);
    105         }
    106     printf("%d
    ",ans);
    107 }
    C送外卖
    时间限制 : - MS   空间限制 : 365536 KB 
    评测说明 : 时限1000ms
    问题描述

      暑期期间,何老板闲来无事,于是买了辆摩托车,签约某团外卖,跑起来送外卖的业务。
      何老板负责的区域里有n个住宅小区(编号1到n),小区间通过m条双向道路相连,两个小区间最多只有一条道路相连,也不存在某小区自己到它自己的道路。每条道路有一定的长度。
      何老板先到1号小区的餐馆去领餐,然后去k个小区送餐(编号2,3,4,...,k+1),最终到n号小区的加油站去给摩托车加油。要到k个小区去送餐,根据下单时间,公司规定了其中某些小区送餐的先后顺序,比如i小区的餐必须在给j小区送餐前送到。何老板希望在满足公司要求的情况下,使得行走的总路程最少,请你帮他计算一下。
      例如,下图所示,起点为1号终点为8号小区。期间要给2、3、4、5号小区送餐。公司规定,给2号小区送餐后才能给3号小区送餐,给3号小区送餐后才能给4、5号小区送餐。最短的行程方案是1—>2—>4—>3—>4—>5—>8,总路程为19。


       注意,可以先经过某些后送餐的小区而不停下来给它们送餐。假如,要送4号小区后才能给3号小区送餐,何老板可以从2号经过3号到达4号小区,中间虽然经过了3号小区,但他没有停下来,这样就不违法公司的规定了。

    输入格式

    第一行,3个空格间隔的整数n,m,k

       接下来m行,每行三个整数x,y,z表示小区x也小区y间有一条长度为z的道路(1<=x,y<=n   1<=z<=1000)

       接下来一行,一个整数t,表示公司有t条要求(0<=t<=k*(k-1)/2)

       接下来t行,每行两个整数x和y,表示给x小区送餐后才能给y号小区送餐
       (2<=x,y<=k+1   x!=y)

    输出格式

    一行,一个整数,表示所求最短总路程。

    样例输入 1

    8 15 4
    1 2 3
    1 3 4
    1 4 4
    1 6 2
    1 7 3
    2 3 6
    2 4 2
    2 5 2
    3 4 3
    3 6 3
    3 8 6
    4 5 2
    4 8 6
    5 7 4
    5 8 6
    3
    2 3
    3 4
    3 5

    样例输出 1

    19

    样例输入 2

    8 7 6
    1 2 10
    2 3 15
    3 4 1
    4 5 12
    5 6 13
    6 7 123
    7 8 10
    5
    7 2
    2 6
    6 3
    3 5
    5 4

    样例输出 2

    588

    提示

    对于100%的数据 2<=N<=20000, 1<=M<=200000, 0<=K<=20

    最短路+状压DP
    通过k+1次dijkstra预处理得到dis[i][j]为点i到点j处理出的最短路
    f[j][i]表示到达i号点的最小代价,到达i号点时的访问状态为j(j为二进制的状压,记录目前已经访问过的点的集合)
    f[s][p]=min{f[j][i]+dis[i][p]}
    其中s=j|(1<<p-1) 且 (j&Before[p])==Before[p]
    j满足Before[p]的所有前提要求
    Before[p]记录到达p号点前应该具备的前提状态

      1 #include <cstdio>
      2 #include <vector>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <queue>
      6 using namespace std;
      7 
      8 #define Node pair<int, int>//<距离,编号> 
      9 const int maxk=22;
     10 const int INF=1000000000;
     11 const int maxm=200005;
     12 const int maxn=20005;
     13 
     14 struct Edge 
     15 {
     16     int End, w, Next;
     17     Edge(int End=0, int w=0, int Next=0):End(End),w(w),Next(Next){};//构造函数,给End,w,Next赋初值 
     18 };
     19 
     20 int n, m, k, All,tot;
     21 int Last[maxn],dis[maxk][maxk],f[1048576][maxk],Before[maxk],bin[maxk],d[maxn];
     22 bool Mark[maxn];
     23 Edge e[maxm<<1];
     24 
     25 int read() 
     26 {
     27     int x=0, f=1;char ch=getchar();
     28     while(ch<'0'||ch>'9'){if(ch == '-')f=-1;ch=getchar();};
     29     while (ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar();}
     30     return x*f;
     31 }
     32 
     33 void add_edge(int u, int v, int w) 
     34 {
     35     e[++tot].End=v, e[tot].w=w, e[tot].Next=Last[u], Last[u]=tot;
     36 }
     37 
     38 // 求第(1 ~ k+1)号节点到其余节点的单源最短路
     39 void dijkstra(int x) 
     40 {
     41     priority_queue<Node,vector<Node>,greater<Node> >Q;
     42     for(int i=1;i<=n;i++) d[i]=INF,Mark[i]=0; 
     43     d[x]=0;
     44     Q.push(make_pair(0,x));//起点进堆(距离,编号)
     45     while(!Q.empty()) 
     46     {
     47         int cur=Q.top().second;   Q.pop();
     48         if(Mark[cur])continue;else Mark[cur]=1;
     49         for(int i=Last[cur];i!=0;i=e[i].Next) 
     50         {
     51             if (d[cur]+e[i].w < d[e[i].End]) 
     52             {
     53                 d[e[i].End]=d[cur]+e[i].w;
     54                 Q.push(make_pair(d[e[i].End], e[i].End));
     55             }
     56         }
     57     }
     58     for(int i=1;i<=k+1;i++)dis[x][i]=d[i];
     59     dis[x][k+2]=d[n];  //为节约空间,dis数组大小为dis[22][22],故用k+2位置来记录x到达n的距离 
     60 }
     61 
     62 void dp() 
     63 {
     64     int i,j,p,S;
     65     for(j=0;j<=All;j++)                        //枚举节点集合
     66         for(i=1;i<=k+1;i++)                    //枚举i号点,目前到达i号点时的状态为j 
     67         { 
     68             if(f[j][i]==-1) continue;          //不存在此状态的表示的路径则返回
     69             for(p=2;p<=k+1;p++)                //枚举从i出发下一个到达的点p 
     70             {  
     71                 S=(j|bin[p-2]);               //将p号节点纳入路径集合
     72                 if ((j&Before[p])==Before[p]) //判断在p节点送餐前是否已经在题目要求的所有前提节点送餐过
     73                     if (f[j][i]+dis[i][p]<f[S][p] || f[S][p] == -1) 
     74                         f[S][p]=f[j][i]+dis[i][p];// 更新最优值 
     75             }
     76         }
     77 }
     78 
     79 int main() 
     80 {
     81     freopen("deliver.in","r",stdin);
     82     freopen("deliver.out","w",stdout);
     83     int i,j,u,v,w,t,ans=INF;
     84     bin[0]=1;
     85     for(i=1;i<22;i++) bin[i]=bin[i-1]<<1;  //bin[i]表示(i-2)号节点是否纳入路径,因为k个小区编号是2到k+1,把2当做0点处理,k+1当做k-1号点处理 
     86     n=read(); m=read(); k=read();          //这样全集All的值比较小,减少f[][]数组的空间 
     87     All=bin[k]-1;// All表示2 ~ (k+1)号节点的全集
     88     for(i=1;i<=m;i++) // 建图
     89     { 
     90         u=read();  v=read();  w=read();
     91         add_edge(u,v,w);
     92         add_edge(v,u,w);
     93     }
     94     for(i=1;i<=k+1;i++)dijkstra(i);// 求出1 ~ (k+1)号节点到其余节点的单源最短路
     95     t=read();
     96     for(i=1;i<=t;i++) 
     97     {
     98         u=read();   v=read();  //Before[v]记录到达v号点前应满足的集合状态 
     99         Before[v]+=bin[u-2];   //表示u号(用二进制的第u-2位表示)节点在v号节点前送餐
    100     }
    101     memset(f,-1,sizeof(f));
    102     f[0][1]=0;// 初始化,f[j][i]表示经过i集合(范围2 ~ k+1)中所有节点且当前送餐在i号节点的最短路
    103     dp();// 求出走完前(1 ~ k+1)号节点最终送餐在第i号节点的最短路
    104     ans=INF;
    105     for(i=1;i<=k+1;i++)
    106         if(f[All][i]!=-1)ans=min(ans,f[All][i]+dis[i][k+2]);// 找出走完(1 ~ k+1)号节点送餐在i到n的最短路
    107     printf("%d",ans);// 输出                     //dis[i][k+2]记录i到终点n的最短距离 
    108 }
  • 相关阅读:
    (转)Android 升级 ADT 之后报错之一 case语句 .
    myeclipse CTRL+1功能
    (转)项目延期原因及应对之道
    shiro的使用2 灵活使用shiro的密码服务模块
    shiro的使用1 简单的认证
    最全的 eclipse web 项目目录结构以及Tomcat的各个目录的作用
    Eclipse导入git上的maven web项目 以及部署成功运行
    eclipse中怎么导入git库下载下来的web项目
    git和github的关系以及简单易懂的理解
    LINUX 怎么实现root和普通用户的切换及怎么更改root密码
  • 原文地址:https://www.cnblogs.com/CXYscxy/p/11152170.html
Copyright © 2011-2022 走看看