刷题总结——保留道路(ssoj)

题目:

题目背景

161114-练习-DAY1-AHSDFZ T3

题目描述

很久很久以前有一个国家,这个国家有 N 个城市,城市由 1,2,3,…,,N 标号,城市间有 M 条双向道路,每条道路都有两个属性 g 和 s ,两个城市间可能有多条道路,并且可能存在将某一城市与其自身连接起来的道路。后来由于战争的原因,国王不得不下令减小花费从而关闭一些道路,但是必须要保证任意两个城市相互可达。

道路花费的计算公式为 wG*max{所有剩下道路的属性g}+wS*max{所有剩下道路的属性s},其中 wG 和 wS 是给定的值。国王想要在满足连通性的前提下使这个花费最小,现在需要你计算出这个花费。

输入格式

第一行包含两个正整数 N 和 M 。
第二行包含两个正整数 wG 和 wS 。
后面的 M 行每行描述一条道路,包含四个正整数 u,v,g,s,分别表示道路连接的两个城市以及道路的两个属性。

输出格式

输出一个整数,表示最小花费。若无论如何不能满足连通性,输出 -1 。

样例数据 1

输入  [复制]

3 3 
2 1 
1 2 10 15 
1 2 4 20 
1 3 5 1

输出

30

备注

【数据规模与约定】
对于 10% 的数据,N≤10;M≤20;
对于 30% 的数据,N≤100;M≤1000;
对于 50% 的数据,N≤200;M≤5000;
对于 100% 的数据,N≤400;M≤50000;wG,wS,g,s≤1000000000

题解:

30 分做法:
按照 g 属性从小到大排序,枚举 maxG,对满足 maxG 的所有道路
按照 s 属性从小到大排序,然后做 kruskal,时间复杂度O(M^2logM)。

50 分做法:
在 30 分基础上,发现每次只增加一条边,插入到上次的边集合中再做 kruskal 即可,时间复杂度 O(M^2)。

100 分做法:
依旧按照 g 属性从小到大排序。丌断加入新边的过程中发现,当前的最小生成树只可能是由未加入新边的最小生成树的边和当前新边组成的共 N 条边中选出 N-1 条构成。因此维护一个最小生成树边集,每次只在 N 条边中做最小生成树,时间复杂度 O(MN)。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=50001;
struct node
{
  int go;
  int to;
  int g;
  int s;
}ed[N];
int n,m,fa[401];
int sta[401],tot,num;
long long wG,wS;
long long ans=2e18+5;
inline int Ri()
{
  char c;
  int i=1,f=0;
  for(c=getchar();(c>‘9‘||c<‘0‘)&&c!=‘-‘;c=getchar());
  if(c==‘-‘)
  {
    i=-1;
    c=getchar();
  }
  for(;c<=‘9‘&&c>=‘0‘;c=getchar())
    f=(f+(f<<2)<<1)+(c^‘0‘);
  return f*i;
}
inline long long Rl()
{
  char c;
  long long i=1,f=0;
  for(c=getchar();(c>‘9‘||c<‘0‘)&&c!=‘-‘;c=getchar());
  if(c==‘-‘)
  {
    i=-1;
    c=getchar();
  }
  for(;c<=‘9‘&&c>=‘0‘;c=getchar())
    f=(f+(f<<2)<<1)+(c^‘0‘);
  return f*i;
}
inline int getfa(int x)
{
  if(fa[x]==x)  return x;
  else return getfa(fa[x]);
}
inline void comb(int x,int y)
{
  int Fx=getfa(x);
  int Fy=getfa(y);
  if(Fx!=Fy)
    fa[Fx]=Fy;
}
bool comp(node a,node b)
{
  if(a.g==b.g) return a.s<b.s;
  return a.g<b.g;
}
int main()
{
  //freopen("a.in","r",stdin);
  n=Ri();
  m=Ri();
  wG=Rl();
  wS=Rl();
  for(int i=1;i<=m;i++)
  {
    ed[i].go=Ri();
    ed[i].to=Ri();
    ed[i].g=Ri();
    ed[i].s=Ri();
  }
  sort(ed+1,ed+m+1,comp);
  tot=0;
  int j;
  for(int i=1;i<=m;i++)
  {
    for(j=1;j<=n;j++)
      fa[j]=j;
    for(j=tot;j>=1;j--)
    {
      if(ed[sta[j]].s>ed[i].s)
        sta[j+1]=sta[j];
      else
        break;
    }
    tot++,sta[j+1]=i;
    num=0;
    for(j=1;j<=tot;j++)
    {
      int Fg=getfa(ed[sta[j]].go);
      int Ft=getfa(ed[sta[j]].to);
      if(Fg!=Ft)
      {
        fa[Fg]=Ft;
        sta[++num]=sta[j];
      }
    }
    if(num==n-1)
      ans=min(ans,ed[sta[num]].s*wS+ed[i].g*wG);
    tot=num;
  }
  if(ans==2e18+5)  cout<<"-1"<<endl;
  else cout<<ans<<endl;
  return 0;
}
时间: 2024-11-01 08:20:40

刷题总结——保留道路(ssoj)的相关文章

刷题总结——选课(ssoj树形dp+记忆化搜索+多叉树转二叉树)

题目: 题目描述 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了 N(N<300)门的选修课程,每个学生可选课程的数量 M 是给定的.学生选修了这M门课并考核通过就能获得相应的学分.在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修.例如<Frontpage>必须在选修了<Windows操作基础>之后才能选修.我们称<Windows操作基础>是<Frontpage

刷题总结——art2(ssoj)

题目: 题解: o(n)复杂度扫一遍再用一个stack维护就可以了·····mdzz这道题都不会做·· 代码: #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<a

用js刷题的一些坑

leecode可以用js刷题了,我大js越来越被认可了是吧.但是刷题中会因为忽略js的一些特性掉入坑里.我这里总结一下我掉过的坑. 坑1:js中数组对象是引用对象 js中除了object还有数组对象也是引用对象,这点常常被忽视,所以在递归的时候传递数组要用arr.slice(0)这样复制一个一样的新数组,不然会出现你传入的数组会被同级的递归改变,结果就不对了. 所以只要数组复制的地方最好都要这么写,除非你真的想引用.而且注意是slice不是splice这两个方法差别很大,你如果用splice(0

XDOJ 1020 ACMer去刷题吧(基础dp)

题目描述 刷题是每个ACMer必由之路,已知某oj上有n个题目,第i个题目小X能做对的概率为Pi(0<=Pi<=1,1<=i<=n) 求小X至少做对k道题的概率 输入 第一行输入一个正整数t,(t<=20),表示有t组测试样例. 第二行输入正整数n,k,(1<=n,k<=1000) 第三行输入n个小数,分别为Pi(1<=i<=n,0<=Pi<=1),表示小X做对第i个题目的概率. 输出 输出小X至少做对k道题的概率,并换行(保留4位小数)

第十六周oj刷题——Problem D: B 友元类-计算两点间距离

Description 类Distance定义为类Point的友元类来实现计算两点之间距离的功能. Point类中有两个私有数据成员X和Y来表示点的两个坐标(横坐标和纵坐标), 成员函数需要自己定义. 主程序输入两个Point点的坐标,计算两个点之间的距离. Input 两个点的坐标(横坐标和纵坐标) Output 两个点的距离(保留了两位小数) Sample Input 1.0 1.0 2.0 2.0 Sample Output 1.41 /* All rights reserved. * 文

oj刷题——第十五周C++习题 对象转换

Description 定义一个Teacher(教师)类(教师号,姓名,性别,薪金)和一个Student(学生)类(学号,姓名,性别,成绩),二者有一部分数据成员是相同的,num(号码),name(姓名),sex(性别).编写程序,将一个Student对象(学生)转换为Teacher(教师)类,只将以上3个相同的数据成员移植过去.可以设想为: 一位学生大学毕业了,留校担任教师,他原有的部分数据对现在的教师身份来说仍然是有用的,应当保留并成为其教师数据的一部分. Input 一个教师的信息和一个学

leetcode 刷题之路 76 Remove Duplicates from Sorted List

Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given 1->1->2, return 1->2. Given 1->1->2->3->3, return 1->2->3. 删除排序链表中重复的节点,删除操作完成后,原链表中的重复节点只保留一个. 思路,遍历链表,如果当前节点和下一个节点重复

HDU 自动刷题机 Auto AC (轻轻松松进入HDU首页)

前言: 在写这篇文章之前,首先感谢给我思路以及帮助过我的学长们 以下4篇博客都是学长原创,其中有很多有用的,值得学习的东西,希望能够帮到大家! 1.手把手教你用C++ 写ACM自动刷题神器(冲入HDU首页) 2.[C#] 逆袭--自制日刷千题的AC自动机攻克HDU OJ 3.C#利用POST实现杭电oj的AC自动机器人,AC率高达50%~~ 4.继续Node爬虫 - 百行代码自制自动AC机器人日解千题攻占HDOJ 当初抱着想试试的想法,用过他们的程序,嗯~ o(* ̄▽ ̄*)o. 1.比如那个用C

python部落刷题宝学到的内置函数(二)

感觉到刷题宝有一个好处,也许也不是好处,它的答案必须是真正输出的值,也就是说应该输出字符串aaaa的时候,答案必须写成界面上返回的值,即'aaaa'.有利于真正记忆返回值类型,但是....太繁琐了 1.getattr():python自省函数,用于查看某对象是否具有某种属性并返回属性值或者末字符串,参数格式(一个对象, 属性名称字符串, 不存在时输出的字符串),举个例子: 1 class A: 2 def __init__(self): 3 self.name = 'hahahaha' 4 a