4.26代码:1dfs+强剪枝 2复习dij板子-错点

1. dfs+强剪枝

题目链接:Wow! Such Conquering!

题目大意:

给出1-n(<30)个点的地图,求一条路径使得经过所有点(每个点一次),并且使得路径距离和最小——注意:这里的路径和是每个点的累加:比如1-2-3-4;距离和:1-2,1-2-3,1-2-3-4 所有距离和。

解决:

floyd松弛所有边;
暴搜找最优解;
强剪枝——技巧!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define db(a, b) cout<<a<<" " <<b<<endl
using namespace std;
const int maxn = 2e6+10;
typedef long long ll;

//思路错误:是求从1到经过其他所有点的一条完整路径的最短距离(说明题意:题目中求的是一条路径上的累加sum),而不是每个最短路径之和---太傻了!
//松弛+dfs+剪枝
int d[45], mp[45][45], pd[45];
int vis[45];
int n;
int ans;

void floyd(){
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(mp[i][j]>mp[i][k]+mp[k][j]){
                    mp[i][j]=mp[i][k]+mp[k][j];
                }
            }
        }
    }
}

void dfs(int u, int cnt, int sum){
    //剪枝
    if(sum+vis[u]*(n-cnt)>ans) return;
    //这里的剪枝
    /*if(u>1&&pd[u]<sum){
            ans = inf;
            return;
    }*/
    for(int i = 2; i <= n; i++){
        //思路修正:从此状态深入深搜,肯定经过当前u,之后的i有一个不满足,此条路肯定到不了所有点,所以剪枝了
        if(vis[i] == -1 && vis[u]+mp[u][i]>pd[i]) return;
    }
    if(cnt == n){
        if(ans>sum){
            ans = sum;
        }
        return;
    }
    for(int i = 2; i <= n; i++){
        if(vis[i] == -1){
            vis[i] = vis[u]+mp[u][i];
            dfs(i, cnt+1, sum+vis[i]);
            vis[i] = -1;
        }
    }
}

int main(){
    while(~scanf("%d", &n)){
        ans = inf;
        memset(d, inf, sizeof(d));
        memset(vis, -1, sizeof(vis));
        memset(mp, inf, sizeof(mp));
        for(int i = 1; i <= n; i++){
           for(int j = 1; j <= n; j++){
               scanf("%d", &mp[i][j]);
           }
        }
        for(int i = 2; i <= n; i++){
            scanf("%d", &pd[i]);
        }
        floyd();
        vis[1] = 0;
        dfs(1, 1, 0);

        if(ans!=inf) printf("%d\n", ans);
        else printf("-1\n");
    }
    return 0;
}

2. Dijkstra板子复习
自己错的原因:d[ ]数组,没有根据mp[ ][ ]初始化 ,所以dijkstra就没有开始——找不到第一个最近点(因为vis[ ]标记)。

题目链接:Wow! Such City!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define db(a, b) cout<<a<<" " <<b<<endl
using namespace std;
const int maxn = 2e6+10;
typedef long long ll;

ll x[maxn], y[maxn], z[maxn];
ll d[1005], mp[1005][1005];
int vis[1005];
int n, m;

void dijkstra(){
    for(int i = 0; i < n; i++){
        d[i] = mp[0][i];
    }
    //问题出在这,如果只是d[0]=0,出现问题:只有7,因为直接找一个非起点本身的最近点,如果没有初始化直接到达可进点,就不能运行djkstra
    //自己的问题:初始化不能丢
    vis[0] = 1;
    for(int i = 0; i < n-1; i++){
        int u = -1;
        ll mind = inf;
        for(int j = 0; j < n; j++){
            if(!vis[j] && d[j]<mind){
                mind = d[j];
                u = j;
            }
        }
        if(u == -1){return;}
        vis[u] =1;
        for(int j = 1; j < n; j++){
            if(!vis[j] && d[j]>d[u]+mp[u][j]){
                d[j] = d[u]+mp[u][j];
            }
        }
    }
}

int main(){
    while(~scanf("%d%d%lld%lld%lld%lld", &n, &m, &x[0], &x[1], &y[0], &y[1])){
        memset(d, inf, sizeof(d));
        memset(vis, 0, sizeof(vis));
        memset(mp, inf, sizeof(mp));
        z[0] = (x[0]*90123+y[0])%8475871+1;
        z[1] = (x[1]*90123+y[1])%8475871+1;
        for(int i = 2; i < n*n; i++){
            x[i] = (12345+x[i-1]*23456+x[i-2]*34567+x[i-1]*x[i-2]*45678)%5837501;
            y[i] = (56789+y[i-1]*67890+y[i-2]*78901+y[i-1]*y[i-2]*89012)%9860381;
            z[i] = (x[i]*90123+y[i])%8475871+1;
        }
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                if(i!=j)mp[i][j]=z[i*n+j];
                else mp[i][j] = 0;
                //db("aaaaa---", mp[i][j]);
            }
        }
        //Dijkstra有问题吗?

        dijkstra();
        ll ans = inf;
        for(int i = 1; i < n; i++){
            ans = min(d[i]%m, ans);
        }
        printf("%lld\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值