【GDOI2014模拟】旅行 题解&代码

10 篇文章 0 订阅
8 篇文章 0 订阅

原题

Description

从前有一位旅者,他想要游遍天下所有的景点。这一天他来到了一个神奇的王国:在这片土地上,有n个城市,从1到n进行编号。王国中有m条道路,第i条道路连接着两个城市ai,bi,由于年代久远,所有的道路都已经不能使用。如果要修复第i条道路,需要wi的时间。为了更好的旅行,旅者想要将某些道路修复,使得1号城市能够到达n号城市,2号城市能够到达n-1号城市..k号城市能够到达n-k+1号城市。为了满足他的要求,请问最少需要多少时间去修复道路。无解请输出-1。

Input

第一行:n,m,k

接下来m行:ai,bi,wi

含义如上所述。

Output

输出共一行:最少需要多少时间修复道路。如果始终无法满足旅者的要求,请输出-1。

Sample Input

5 5 2

1 3 4

3 5 2

2 3 1

3 4 4

2 4 3

Sample Output

9

Data Constraint

20%的数据满足:k <= 2, n<= 10, m <= 20

40%的数据满足:k <= 3, n<=100, m<=1000

70%的数据满足:k<=4, n<=1000, m<=1000

100%的数据满足:k<=4, n<=10000, m<=10000, n >= 2*k, wi<= 1000, 1 <= ai, bi <= n

Hint

样例解释:
这里写图片描述

题目大意

  • 给出n个点,m条带权双向边,和一个整数k。

  • k4,n10000,m10000

  • 让你连接其中一些边,使得1号城市能够到达n号城市,2号城市能够到达n-1号城市…k号城市能够到达n-k+1号城市。

  • 最少的边权。若不能满足则输出-1

题解

  • 用每一种1~k的顺序(即 K的全排列)来对每一个 i(1 i k)操作。

  • 用单源最短路算法SPFA求出点 i 到点 n-i+1 的最短距离。

  • 再把最短路径上的每一条边的权值赋为0。

  • 统计最小答案即可。以下为代码:

Code

#include<cstdio>
#include<cstring>
using namespace std;
const int N=10001;
int ans,tot,dis[N],f[N],que[3*N],h[24][4];
int first[N],next[2*N],en[2*N],w[2*N],w1[2*N];
bool bz[N];
inline int read()
{
    int data=0; char ch=0;
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
    return data;
}
inline void add(int x,int y,int z)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline void dfs(int x,int y)
{
    if(x==y)
    {
        for(int i=0;i<y;i++) h[tot][i]=f[i+1];
        tot++;
        return;
    }
    for(int i=1;i<=y;i++)
        if(!bz[i])
        {
            bz[i]=true;
            f[++f[0]]=i;
            dfs(x+1,y);
            f[f[0]--]=0;
            bz[i]=false;
        }
}
int main()
{
    int n=read(),m=read(),q=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read(),z=read();
        add(x,y,z);
        add(y,x,z);
    }
    tot=0;
    dfs(0,q); //全排列
    ans=1e7;
    for(int i=0;i<tot;i++)
    {
        int sum=0,w1[2*N];
        memcpy(w1,w,sizeof(w1));
        for(int j=0;j<q;j++)
        {
            memset(dis,60,sizeof(dis));
            memset(bz,false,sizeof(bz));
            memset(f,0,sizeof(f));
            int head=dis[h[i][j]]=0,tail=1;
            que[tail]=h[i][j];
            while(head<tail)
            {
                bz[que[++head]]=false;
                for(int k=first[que[head]];k;k=next[k])
                    if(dis[en[k]]>dis[que[head]]+w1[k])
                    {
                        dis[en[k]]=dis[que[head]]+w1[k];
                        f[en[k]]=que[head];
                        if(!bz[en[k]]) bz[que[++tail]=en[k]]=true;
                    }
            } //此处为SPFA
            if(dis[n-h[i][j]+1]>1e7)
            {
                printf("-1");
                return 0;
            } //无解
            sum+=dis[n-h[i][j]+1];
            for(int t=n-h[i][j]+1;t!=f[h[i][j]];t=f[t])
            {
                for(int k=first[f[t]];k;k=next[k])
                    if(en[k]==t)
                    {
                        w1[k]=0;
                        break;
                    }
                for(int k=first[t];k;k=next[k])
                    if(en[k]==f[t])
                    {
                        w1[k]=0;
                        break; 
                    }
            } //边赋为0
        }
        if(sum<ans) ans=sum;
    }
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值