18寒假最后一测+dijistra模板

题目名称

run

homework

cut

输入

run.in

homework.in

cut.in

输出

run.out

homework.out

cut.out

每个测试点时限

1秒

1秒

2秒

内存限制

64MB

64MB

64MB

测试点数目

10

10

10

每个测试点分值

10

10

10

是否有部分分

题目类型

传统

传统

传统

 

run

Background:

        穿过时空隧道,小明来到了秦朝。我是谁?我在哪?

            “哈哈哈,金科,发什么呆呢,你的刺杀已经失败了!”秦王躲在柱子后狂妄的笑。

            一股热血在小明体内流动,他竟情不自禁地追了上去。

Description:

        宫殿里一共有n根柱子,假设柱子在二维平面内的位置为(xi,yi)。金科现在在一号柱子,秦王现在在n号柱子。金科(小明)会飞檐走壁所以他从i号柱子到j号柱子的距离为min(|xi-xj|,|yi-yj|)。深知自己并不能刺杀秦王,所以小明知道事实上自己竭尽全力,也离秦王有一步之遥,所以小明想知道自己到达n号柱子的最短距离-1是多少,因为这将是他人生能走过的最后的路。

Input:

        第一行一个数n。

            接下来n-1行每行两个数x,y分别表示xi和yi。

Output:

            一行一个数表示从1号柱子走到n号柱子的最短距离减去1的长度。

Sample input:

5

2 2

1 1

4 5

7 1

6 7

Sample output:

1

Hint:

1->2->4->5最短距离为2,答案为1。

对于30%的数据,n<=1000。

对于100%的数据,n<=200000,0<xi,yi<1e9

数据保证答案>0,不然很奇怪。。

 题解:我们发现每个点分别于横纵坐标相邻的点连边就可以解决问题了,建边后直接跑最短路就可以了。最后不要忘记答案-1.(可以自己构图验证正确性)

 用堆优化+dijistra的板子

#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
#define maxn 200005
#define maxm 1000005
bool vis[maxn];
int L,n,tot,head[maxn],dis[maxn];
inline void read(int &x){
    x=0;int f=1;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
struct Point{
    int x,y,id;
}p[maxn];
struct edge{
    int nxt,v,w;
}G[maxm];
struct rp{
    int id,key;
    rp(int a,int b){
        id=a, key=b;
    }
};
bool cmp2(Point a, Point b){
    return a.x < b.x;
}
bool cmp1(Point a, Point b){
    return a.y < b.y;
}
struct cmp{
    bool operator()(const rp& a, const rp& b){
        return a.key > b.key;
    }
};

void add(int u, int v, int w){
    G[++tot].nxt = head[u];
    G[tot].w = w;
    G[tot].v = v;
    head[u] = tot;
    G[++tot].nxt = head[v];
    G[tot].w = w;
    G[tot].v = u;
    head[v] = tot;
}
void dijistra(){
    memset(dis, 127, sizeof(dis));
    priority_queue <rp,vector<rp>,cmp> q;
    q.push(rp(1, 0));
    dis[1] = 0;
    
    while(!q.empty()){
        rp u = q.top();
        q.pop();
        if(vis[u.id])continue;
        vis[u.id] = 1;
        for(int j = head[u.id]; j; j = G[j].nxt){
            int v = G[j].v;
            if(dis[v] > dis[u.id] + G[j].w){
                dis[v] = dis[u.id] + G[j].w;
                q.push(rp(v, dis[v]));
            }
        }
    }
}
int main()
{    
    freopen("run.in","r",stdin);
    freopen("run.out","w",stdout);
    read(n);
    for(int i = 1; i <= n; i++)
        read(p[i].x), read(p[i].y), p[i].id = i;
    sort(p+1, p+1+n, cmp2);
    for(int i = 1; i < n; i++)
        add(p[i].id, p[i+1].id, p[i+1].x - p[i].x);
    sort(p+1, p+1+n, cmp1);
    for(int i = 1; i < n; i++)
        add(p[i].id, p[i+1].id, p[i+1].y - p[i].y);
    dijistra();
    printf("%d\n",dis[n]-1);
}

 

 

 

homework

Background:

        小哪吒在开学前一天补作业。。。

Description:

        明天就要开学了!所以小哪吒需要尽快写完作业。现在,他有n份作业需要写,因为小哪吒精通变化,他可以再变出两只手来,所以他可以同时写两份作业。小哪吒两双手写同一份作业的时间是不一样的。为了节约时间,他用火眼睛睛看出了两双手写同一份作业分别需要ai,bi的时间。同样的,为了节约时间,他不会更改做题的顺序,即他会从第一份作业一直做到第n份作业。他唯一不知道的是,他能写完作业的最短时间。你能帮他求出来吗?

Input:

        第一行一个数n表示作业的数目。

            接下来n行每行两个数ai,bi分别表示小哪吒两双手写这份作业的时间。

Output:

        一行一个数表示写作业的最短时间。

Sample input:

3
1 3
1 3
1 3

Sample output:

    3

Hint:

            对于样例,用第一双手做完所有作业。

            对于40%的数据,n<=20。

            对于100%的数据,n<=2000。ai,bi<=3000。

 题解:我们用dp[i][0/1][j]表示去做第i份作业用第0/1只手时,另外一双手还需要做j的时间才能做完作业的最小时间;

dp[i][j][k] = min( dp[i - 1][ j ][ k+a[i - 1][ j ] ] + a[i - 1][ j ],  dp[i - 1][ j^1 ][ a[i - 1][ j^1 ] ] , 但我们有可能在0手有空时不一定用0手而是等1手完了用1手,

而这样当 k+a[i - 1][ j ]  = 0我们刚好就去用了空闲的手,故我们反着推就可以了,因为dp[i][j][k] k=0 时有可能那只手已经休息了很久了;

注意dp[i]中第i份作业还没做,所以我们最后要加上剩下的时间

#include <bits/stdc++.h>

using namespace std;
#define maxn 2005
int dp[maxn][2][3005], a[maxn][2];
inline void read(int &x){
    x=0;int f=1;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int main(){
    ///freopen("homework.in","r",stdin);
    //freopen("homework.out","w",stdout);
    int n;
    read(n);
    for(int i = 1; i <= n; i++)
        read(a[i][0]), read(a[i][1]);
    memset(dp, 0x3f, sizeof(dp));
    dp[1][0][0] = dp[1][1][0] = 0;
    for(int i = 1; i <= n; i++)
    for(int j = 0; j < 2; j++)
    for(int k = 0; k <= 3000; k++){
        dp[i+1][j][max(0, k-a[i][j])] = min(dp[i+1][j][max(0, k-a[i][j])], dp[i][j][k]+a[i][j]);
        dp[i+1][j][max(0, a[i][j^1]-k)] = min(dp[i+1][j][max(0, a[i][j^1]-k)] , dp[i][j^1][k]+k);
    }
    int ans = 1e9;
    for(int j = 0; j < 2; j++)
    for(int k = 0; k <= 3000; k++)
        ans = min(ans, dp[n][j][k] + max(k, a[n][j]));
    printf("%d\n",ans);
}

 

cut

Background:

        伐木工Zxr非常懒惰了,所以他在伐木的时候只会找准木头被虫蛀过的腐朽的地方砍下去。

Description:

        一块有N-1处被虫蛀过的地方,假设被虫蛀过的地方长度不计。这n-1个虫蛀将木头分成了n块,题目将依次给出这n块木头的长度。懒惰的zxr最多只想砍m次,并且希望可以借此把这块木头分得尽量均匀,即希望使砍伐后连续的木块中最长的尽量短。这个时候聪明的你跳了出来“我不仅能算出这个最短长度,我还能算出方案数!”

Input:

            第一行两个数n,m。

            接下来一行n个数分别表示这N块木头的长度。

Output:

            一行两个数分别表示最短的最长长度和方案数。

Sampleinput:

            3 2

            1 1 10

Sampleoutput:

            10 2

Hint:

            两种砍的方法: (1)(1)(10)和(1 1)(10)。

            对于30%的数据,n<=10,

            对于100%的数据,n<=50000,

0<=m<=min(n-1,1000),1<=Li<=1000.

题解:

第一问是一个十分显然的二分,贪心Check(),很容易就能求出最小的最大长度 Len 。

第二问求方案总数,使用 DP 求解。

  使用前缀和,令 Sum[i] 为前i根木棍的长度和。

  令 f[i][j] 为前i根木棍中切 j 刀,并且满足最长长度不超过 Len的方案数,那么:

    状态转移方程: f[i][j] = Σ f[k][j-1]   ((1 <= k <= i-1) &&  (Sum[i] - Sum[k] <= Len))  

  这样的空间复杂度为 O(nm) ,时间复杂度为 O(n^2 m) 。显然都超出了限制。

  下面我们考虑 DP 的优化。

  1) 对于空间的优化。

    这个比较显然,由于当前的 f[][j] 只与 f[][j-1] 有关,所以可以用滚动数组来实现。

    f[i][Now] 代替了 f[i][j] , f[i][Now^1] 代替了 f[i][j-1] 。为了方便,我们把 f[][Now^1] 叫做 f[][Last] 。

    这样空间复杂度为 O(n) 。满足空间限制。

  2) 对于时间的优化。

    考虑优化状态转移的过程。

    对于 f[i][Now] ,其实是 f[mink][Last]...f[i-1][Last] 这一段 f[k][Last] 的和,mink 是满足 Sum[i] - Sum[k] <= Len 的最小的 k ,那么,对于从 1 到 n 枚举的i,相对应的 mink 也一定是非递减的(因为 Sum[i] 是递增的)。我们记录下 f[1][Last]...f[i-1][Last] 的和Sumf,mink 初始设为 1,每次对于i将 mink 向后推移,推移的同时将被舍弃的 p 对应的 f[p][Last] 从Sumf中减去。那么 f[i][Now] 就是Sumf的值。

    这样时间复杂度为 O(nm) 。满足时间限制。

#include <bits/stdc++.h>

using namespace std;
#define maxn 50005
#define mod 10007
int dp[maxn][2], a[maxn], sum[maxn], L, n, m, Ml;
inline void read(int &x){
    x=0;int f=1;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
    while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

bool check(int mid){
    int ll = 0, i = 0, group = 1, flag = 0;
        while(i <= n){
            if(ll + a[++i] <= mid)ll += a[i];
            else if(a[i] > mid)return false;
            else {
                group++;
                if(group > m + 1)return false;
                ll = a[i];
            }
        }
    return true;
}
void bs(){
    int l = Ml, r = sum[n];
    while(l != r){
        
        int mid = (l + r) >> 1;
        if(check(mid))r = mid;
        else l = mid+1;
        
    }
    L = r;//取大的
    printf("%d ",L);
}
void Dp(){
    int sumf = 0, mink = 0, ans = 0;
    int k = 0;
    for(int j = 0; j <= m; j++){
        sumf = 0; mink = 1;//一定要记得更新,忘了更新这个地方,WA了好多次
        for(int i = 1; i <= n; i++){
            if(!j){
                if(sum[i] <= L)dp[i][k] = 1;
            }
            else {
                while(mink < i && sum[i] - sum[mink] > L){
                        sumf -= dp[mink][k^1];
                        sumf = (sumf + mod) % mod;
                        mink++;            
                    }    
                dp[i][k] = sumf;
            }           
            
            sumf = (sumf + dp[i][k^1]) % mod;
        }
        ans += dp[n][k];
        ans %= mod;
        k^=1;//滚动数组
    }
    
    printf("%d\n",ans);
}
int main(){
    freopen("cut.in","r",stdin);
    freopen("cut.out","w",stdout);
    read(n),read(m);
    for(int i = 1; i <= n; i++){
        read(a[i]);
        Ml = max(Ml, a[i]);
        sum[i] = sum[i-1] + a[i];
    }
    bs();
    Dp();
}

 

转载于:https://www.cnblogs.com/EdSheeran/p/8496929.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值