首页 > 其他分享 >洛谷题单指南-搜索-P1433 吃奶酪

洛谷题单指南-搜索-P1433 吃奶酪

时间:2024-03-06 12:45:00浏览次数:25  
标签:dist int 洛谷题 奶酪 dfs double P1433 ans

原题链接:https://www.luogu.com.cn/problem/P1433

题意解读:计算经过所有奶酪一次的总路径最短,可以采用dfs、dp等方法。

解题思路:

最直接的思路是DFS,暴搜所有的路径方案,计算最小距离,n最大是15,复杂度为15!≈10^12,必定会超时,先保证正确性,得到部分分:

50分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 20;

double x[N], y[N]; //保存奶酪坐标
int path[N]; //保存路径是第几个奶酪
double jl[205][205]; //保存每两个点之间的距离
bool flag[N]; //标记第i个奶酪是否已走过
int n;

double ans = 2e9;

double distance(double x1, double y1, double x2, double y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

//k:第几个奶酪,dist:当前走过的距离
void dfs(int k, double dist)
{
    if(k > n)
    {
        ans = min(ans, dist);
        return;
    }
    for(int i = 1; i <= n; i++)
    {
       if(flag[i]) continue;
       flag[i] = true;
       path[k] = i;
       dfs(k + 1, dist + jl[path[k]][path[k-1]]);
       flag[i] = false; //恢复flag
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];

    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= n; j++)
        {
            jl[i][j] = distance(x[i], y[i], x[j], y[j]);
        }
    }

    dfs(1, 0);
    cout << fixed << setprecision(2) << ans;

    return 0;
}

能否尽量减少DFS的次数呢?由于是要找最小路径,那么如果在DFS过程中,某次已走过的路径已经大于之前保存的最小路径,则本次dfs可以返回,这样起到了一定的剪枝效果,关键代码是:if(dist > ans) return;

由于剪枝无法准确评估到底能减掉多少,所以复杂度是由数据点来决定的,仍然无法保证不超时,只能尽量减少超时。

90分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 20;

double x[N], y[N]; //保存奶酪坐标
int path[N]; //保存路径是第几个奶酪
double jl[205][205]; //保存每两个点之间的距离
bool flag[N]; //标记第i个奶酪是否已走过
int n;

double ans = 2e9;

double distance(double x1, double y1, double x2, double y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

//k:第几个奶酪,dist:当前走过的距离
void dfs(int k, double dist)
{
    if(dist > ans) return; //如果某次中间的距离已经大于之前的最小ans,直接结束本次搜索
    if(k > n)
    {
        ans = min(ans, dist);
        return;
    }
    for(int i = 1; i <= n; i++)
    {
       if(flag[i]) continue;
       flag[i] = true;
       path[k] = i;
       dfs(k + 1, dist + jl[path[k]][path[k-1]]);
       flag[i] = false; //恢复flag
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];

    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= n; j++)
        {
            jl[i][j] = distance(x[i], y[i], x[j], y[j]);
        }
    }

    dfs(1, 0);
    cout << fixed << setprecision(2) << ans;

    return 0;
}

还能不能进一步减少dfs的次数呢?

想象一种场景,当最多有15个点时,如果已经过a、b、c、d、e 5个点,当前在d点,走过距离是L1

下一次dfs时,也已经过a、b、c、d、e 5个点,当前在b点,走过距离是L2,

如果L2>L1,其实这时就没有必要继续dfs下去,因为肯定不可能是最优解。

因此,我们可以记录每种状态下所走过的距离,通过二进制来表示已经过了哪几个点

如:01011可以表示经过了1/2/4号点,用整数表示就是11,15个点用整数表示状态不超过2^16-1 = 65535

定义数组double dp[40000][20]; dp[i][j]表示,已经过的点是i所表示的二进制状态(1表示走过,0表示没走过)、当前在j点所经过的距离,

在代码中,通过判断dp[i][j]是否有值,且当前值比原值还大,则不需要继续dfs。

100分代码:

 

#include <bits/stdc++.h>
using namespace std;

const int N = 20;

double x[N], y[N]; //保存奶酪坐标
int path[N]; //保存路径是第几个奶酪
double jl[205][205]; //保存每两个点之间的距离
bool flag[N]; //标记第i个奶酪是否已走过
double dp[65540][20]; //dp[i][j]表示已经过的点是i所表示的状态,当前在j点所经过的距离
int n;

double ans = 2e9;

double distance(double x1, double y1, double x2, double y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

//k:第几个,s:当前已经过的点的状态,dist:当前走过的距离
void dfs(int k, int s, double dist)
{
    if(dist > ans) return; //如果某次中间的距离已经大于之前的最小ans,直接结束本次搜索
    if(k > n)
    {
        ans = min(ans, dist);
        return;
    }
   
    for(int i = 1; i <= n; i++) //遍历所有奶酪编号
    {
        if(flag[i]) continue;
 
        int ns = s + (1 << (i - 1)); //当前已经过的点算上i 
        double ndist = dist + jl[i][path[k-1]]; //已经过的距离加上当前距离
        if(dp[ns][i] != 0 && ndist >= dp[ns][i]) continue; //如果当前已经过点的状态之前已出现过,且距离比之前还大或相等,则不需要继续dfs
        
        flag[i] = true; path[k] = i; dp[ns][i] = ndist;
        dfs(k + 1, ns, ndist);
        flag[i] = false; //恢复flag
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];

    //预计算所有点之间的距离
    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= n; j++)
        {
            jl[i][j] = distance(x[i], y[i], x[j], y[j]);
        }
    }

    dfs(1, 0, 0);
    cout << fixed << setprecision(2) << ans;

    return 0;
}

 

标签:dist,int,洛谷题,奶酪,dfs,double,P1433,ans
From: https://www.cnblogs.com/jcwy/p/18056277

相关文章

  • 洛谷题单指南-搜索-P2895 [USACO08FEB] Meteor Shower S
    原题链接:https://www.luogu.com.cn/problem/P2895题意解读:所谓安全格子,就是在所有流星坠落之后,没有被烧焦的格子,要计算从起点到这些格子任一一个的最短路径,BFS可以解决。解题思路:1、读取数据,先把所有流星坠落点以及周围被烧焦的格子进行标记,得到安全格子2、从起点开始BFS,每走......
  • 洛谷题单指南-搜索-P1135 奇怪的电梯
    原题链接:https://www.luogu.com.cn/problem/P1135题意解读:计算A到B至少要按几次电梯,本质上就是求A到B的最短路径,可以通过BFS解决。解题思路:位于每一层,有两种选择:向上、向下BFS搜索直接从A找到B,每扩展一层,层数+1,层数即按电梯次数100分代码:#include<bits/stdc++.h>usingnam......
  • 洛谷题单指南-搜索-P1443 马的遍历
    原题链接:https://www.luogu.com.cn/problem/P1443题意解读:无论是国际象棋还是中国象棋,马的活动范围都是一样的:只不过国际象棋棋子是在格子中,中国象棋棋子是在交点,坐标的变化方式是一样的,根据此活动范围,计算马到达每一个点的最短路径。解题思路:根据马的活动范围,在棋盘内进行B......
  • 洛谷题单指南-搜索-P2392 kkksc03考前临时抱佛脚
    原题链接:https://www.luogu.com.cn/problem/P2392解题思路:参考https://www.cnblogs.com/jcwy/p/18003097前面已经给出了二进制法的代码,这里给出DFS的代码100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=25;ints1,s2,s3,s4;inta[N],b[N],c[......
  • 洛谷题单指南-搜索-P1219 [USACO1.5] 八皇后 Checker Challenge
    原题链接:https://www.luogu.com.cn/problem/P1219题意解读:八皇后,经典回溯问题。解题思路:逐行摆放棋子,关键在于如何快速判断行、列、正斜(左上到右下)、反斜(右上到左下)方向没有已放其他棋子1、由于逐行摆放,因此行不需要判断通过一个boolcol[N]数组即可判断列上是否已摆放其他棋......
  • 洛谷题单指南-二分查找与二分答案-P3743 kotori的设备
    原题链接:https://www.luogu.com.cn/problem/P3743题意解读:设备使用的时间越久,需要充电的总时间也越多,具备单调性,根据使用的时间,计算需要充电的时间,如果充电总时间<=使用的时间,说明有电量还能富余,使用时间还可以更多,因此只需对使用时间进行二分即可。解题思路:给定设备使用的时间......
  • 洛谷题单指南-二分查找与二分答案-P1163 银行贷款
    原题链接:https://www.luogu.com.cn/problem/P1163题意解读:利率越小,贷款期限和每个月还的钱固定的情况下,越有可能能够还完全部的贷款,具备单调性,因此给定贷款利率、贷款月数、每月还款钱数,可以计算最终贷款还剩下多少,有两种情况:>=0,说明利率可能大了,要试探更小利率;<0,说明利率小了,要......
  • 洛谷题单指南-二分查找与二分答案-P1182 数列分段 Section II
    原题链接:https://www.luogu.com.cn/problem/P1182题意解读:每段和的最大值越小,则分段数就越多,因此可以通过给定每段和的最大值,将分段数划分为两类:<=M,>M,对每段和的最大值进行二分即可。解题思路:二分的判定条件为,给定每段和的最大值,计算分段数,计算逻辑如下:依次遍历每一个数,求当前......
  • 洛谷题单指南-二分查找与二分答案-P3853 [TJOI2007] 路标设置
    原题链接:https://www.luogu.com.cn/problem/P3853题意解读:相邻路标的最大距离即空旷指数,空旷指数越小,用的路标越多,因此可以根据空旷指数将使用路标情况分成两类:路标数<=K,路标数>K,对空旷指数进行二分即可。解题思路:二分的判定条件为,给定空旷指数,计算需要的路标数只需遍历每两......
  • 洛谷题单指南-二分查找与二分答案-P2678 [NOIP2015 提高组] 跳石头
    原题链接:https://www.luogu.com.cn/problem/P2678题意解读:最短跳跃距离越大,要移走的石头就越多,因此可以根据最短跳跃距离的不同把情况分为两类:移走的石头数<=M、移走的石头数>M,对最短跳跃距离二分即可。解题思路:二分的判定条件如下:对于给定最短跳跃距离,需要计算移走的石头数,......