P2872 [USACO07DEC]Building Roads S

https://www.luogu.com.cn/problem/P2872
在这里插入图片描述
在这里插入图片描述
就是求一个点到其他所有点构成的边,以及其距离是多少。
所以要对应,第 i,j个点。Union的是第i,j这俩序号,而不是点,因为i点由x,y两个坐标,就根据一个

for(int i=1;i<=n;i++) 
	for(int j=i+1;j<=n;j++)

就把每一个点和其他所有边都存进去了。
真腻害
哎,其实自己用bei的话,其实也能想出来。
1 2 3 4 5五个点,求1到其他点的距离,2到其他点的距离,3到其他点的距离,4到其他点的距离,5到其他点的距离,不就等于4+3+2+1=10=54/2=n(n-1)/2的吗。然后怎么变成程序,不就是for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)这样遍历吗。
自己没有意识到是求边的两个点,两个点的距离,而不是存每个点的最短距离。然后就意识不到上面的操作了、
根本就不是看最短距离啊,因为你不知道和这个点连接有最短距离的那个点是谁,应该Union这两个。不就是上面的操作吗。。。
垃圾垃圾垃圾,谁赢了谁赢了,谁赢了哈哈哈
希望,能上岸,能成功,能有一颗少年的心把

//应该是求第i个点到第j个点的距离,而不是直接求最短距离
//思路总有一点偏颇
/*
直接求最短距离,不知道是求的谁的。但是我也不知道哪里错了。哎很烦。
先能写出来对的,再去搞错了的把
*/
/*
后面处理提前设置好的边的时候,再添加一条边,from to都不变,distance变成0
然后sort的时候这个就会排到最前面,然后前面这两个点Union了后面就不会Union了
我实在是想不到。。。。。
*/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
const int N =  5000100;//~~是边的个数。。。。~~怎么来的你不知道? 
int f[N];
int n,m;
struct nod{
    int x,y;
}node[N];
struct Edge{
    int from,to;
    double distance;
}edge[N];
void Init()
{
    for(int i=1;i<N;i++)
        f[i]=i;
}
int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}
void Union(int a,int b)
{
    a=find(a);b=find(b);
    f[b]=a;
}
double getdistance(int i,int j)
{
    int x1,x2,y1,y2;
    x1=node[i].x;x2=node[j].x;
    y1=node[i].y;y2=node[j].y;
    double res;
    res=(double)sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2));
    return res;
}
bool cmp(Edge a,Edge b)
{
    if(a.distance==b.distance) return a.from<b.from;
    return a.distance<b.distance;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&node[i].x,&node[i].y);
    }
    //求第i个点到其余各点的距离。
    int k=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            edge[++k].from=i;
            edge[k].to=j;
            edge[k].distance=getdistance(i,j);
        }
    }
      Init();
  //  for(int i=1;i<=k;i++)cout<<"("<<node[edge[i].from].x<<","<<node[edge[i].from].y<<")到("<<node[edge[i].to].x<<","<<node[edge[i].to].y<<")的距离是"<<edge[i].distance<<endl;
    for(int i=1;i<=m;i++)
    {
        int p,q;
        scanf("%d%d",&p,&q);
        Union(p,q);
        edge[++k].from=p;
        edge[k].to=q;
        edge[k].distance=0;
    }
  
    double res=0;
    sort(edge+1,edge+1+k,cmp);
   // for(int i=1;i<=k;i++)cout<<"("<<node[edge[i].from].x<<","<<node[edge[i].from].y<<")到("<<node[edge[i].to].x<<","<<node[edge[i].to].y<<")的距离是"<<edge[i].distance<<endl;
    for(int i=1;i<=k;i++)
    {
        int a=edge[i].from,b=edge[i].to;
        int roota=find(a),rootb=find(b);
        if(roota!=rootb)
        {
          //  cout<<"当前节点为("<<node[a].x<<","<<node[a].y<<")和("<<node[b].x<<","<<node[b].y<<")的距离为"<<edge[i].distance<<endl;
            res+=edge[i].distance;
            f[roota]=rootb;
        }
    }
    //cout<<endl;
    printf("%.2lf",res);
}

自己写的:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<math.h>
    using namespace std;
    const int N = 5000001; //这个怎么来的?1000*1000/2,你值得拥有。
    struct node{
        int x,y;
    }point[N];
    struct nod{
        int point1,point2;
        double dis;
        bool operator<(const nod &a)
        {
            if(a.dis==dis) return point1<a.point1;
            return dis<a.dis;
        }
    }edge[N];
    int f[N];
    int n,m;
    void Init()
    {
        for(int i=1;i<N;i++) f[i] = i;
    }
    int find(int x)
    {
        if(x==f[x]) return x;
        return f[x] = find(f[x]);
    }
    void Union(int x,int y)
    {
        x=find(x);y=find(y);
        f[x]= y;
    }
    double getdistance(int x1,int y1,int x2,int y2)
    {
        double res;
        res=(double)sqrt((double)(x1-x2)*(x1-x2)+(double)(y1-y2)*(y1-y2));必须这样改,不然不对。好迷。
        return res;
    }
    int main()
    {
        cin>>n>>m;
        Init();
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&point[i].x,&point[i].y);
        }
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            Union(x,y);
        }
    // for(int i=1;i<=n;i++) cout<<f[i]<<" ";cout<<endl;
        int k=0;
        for(int i=1;i<=n;i++)
        {
        for(int j=i+1;j<=n;j++)
            {
                int x1,y1,x2,y2;
                x1=point[i].x,y1=point[i].y;
                x2=point[j].x,y2=point[j].y;
                edge[++k].point1=i;
                edge[k].point2=j;
                edge[k].dis = getdistance(x1,y1,x2,y2);
                
            }
        }
    //  for(int i=1;i<=k;i++) cout<<edge[i].point1<<" "<<edge[i].point2<<" "<<edge[i].dis<<endl;
        double res=0;
        sort(edge+1,edge+1+k);
        for(int i=1;i<=k;i++)
        {
            int a=edge[i].point1,b=edge[i].point2;
            int roota=find(a),rootb=find(b);
            if(roota!=rootb)
            {
                res+=edge[i].dis;
            // cout<<a<<"!="<<b<<" dis="<<edge[i].dis<<endl;;
                Union(roota,rootb);
            }
            //for(int i=1;i<=n;i++) cout<<f[i]<<" ";cout<<endl;
        }
        printf("%.2lf",res);
    }
题目描述 有一个长度为 $n$ 的书架,每本书有一个高度 $h_i$。现在你可以进行以下两种操作: - 将一本书放在书架的最左边或最右边,花费为 $c_1$。 - 将一本高度为 $h_i$ 的书放在一本高度为 $h_j$ 的书的上面,花费为 $c_2$。 现在你需要将书架上的书按照高度从小到大排列,求最小花费。 输入格式 第一行包含三个整数 $n,c_1,c_2$。 第二行包含 $n$ 个整数 $h_i$。 输出格式 输出一个整数,表示最小花费。 数据范围 $1\leq n\leq 200,1\leq c_1,c_2\leq 10^9,1\leq h_i\leq 10^9$ 输入样例 5 1 2 3 1 4 2 5 输出样例 6 算法1 (动态规划) $O(n^2)$ 首先考虑一个朴素的 dp,设 $f_{i,j}$ 表示前 $i$ 本书已经排好序,第 $i+1$ 本书放在第 $j$ 个位置的最小花费。 状态转移方程为: $$ f_{i,j}=\min\{f_{i-1,k}+c_1\}+\begin{cases}&\text{if }h_{i+1}>h_j\\c_2&\text{otherwise}\end{cases} $$ 其中 $k$ 取遍 $1\sim i$,表示将第 $i+1$ 本书放在第 $k$ 个位置。 时间复杂度 $O(n^3)$ C++ 代码 算法2 (单调队列优化) $O(n^2)$ 考虑优化上述 dp,发现状态转移方程中的 $\min$ 操作可以用单调队列优化,具体来说,我们维护一个单调递增的队列 $q$,其中 $q_i$ 表示第 $i$ 个位置的最小花费,那么对于状态 $f_{i,j}$,我们只需要找到 $q$ 中第一个大于等于 $f_{i-1,k}+c_1$ 的位置 $p$,然后 $f_{i,j}=q_p+\begin{cases}&\text{if }h_{i+1}>h_j\\c_2&\text{otherwise}\end{cases}$。 时间复杂度 $O(n^2)$ C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值