2018-08-07 最短路径&差分约束

  • A  -- HDU2544

  • Description

在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

  • Input

输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线

  • Output

对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

  • Sample Input

2 1

1 2 3

3 3

1 2 5

2 3 5

3 1 2

0 0

  • Sample Output

3

扫描二维码关注公众号,回复: 2647362 查看本文章

2

  • 题目理解

题意就是求最短路,由于没有负权边,直接Dijkstra计算就可以了。对于Dijkstra来说第一次出队列的必定是当前时刻最小的路径长度也就意味没有任何一条路径能从已经确认的点(已经确认最短路)到达当前点更短,因为长路径加上任意一条边只会更长所以第一次出队列的点就是单源最短路,以后该点再出队列只会距离更远不再进队列执行松弛操作

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct ver{
   int v,w;
   ver(){}
   ver(int V,int W):v(V),w(W){}
};
struct dist{
   int id,dis;
   dist(){};
   dist(int ID,int DIS):id(ID),dis(DIS){}
   friend bool operator<(const dist& a,const dist& b){
      return a.dis>b.dis;
   }
};
const int maxn=105;
const int inf=0x3f3f3f3f;
vector<ver> edge[maxn];
int ans[maxn],vis[maxn];
void Dijkstra(int s){
   memset(ans,inf,sizeof(ans));
   memset(vis,0,sizeof(vis));
   priority_queue<dist> p;
   p.push(dist(s,0));
   ans[s]=0;
   while(!p.empty()){
     dist cur=p.top();p.pop();
     if(vis[cur.id]) continue;//判断是否有更新价值
     vis[cur.id]=1;
     for(int i=0;i<edge[cur.id].size();++i){
        int v=edge[cur.id][i].v;
        int w=edge[cur.id][i].w;
        if(ans[v]>cur.dis+w){
            ans[v]=cur.dis+w;//记录在更新中得到的最短路
            p.push(dist(v,ans[v]));
        }//得到的最短路去更新所有相邻边,有可能得到最短路
     }
   }
}
int main()
{
    int n,m,u,v,w;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m){
        for(int i=0;i<maxn;++i) edge[i].clear();
        for(int i=0;i<m;++i){
            scanf("%d%d%d",&u,&v,&w);
            edge[u].push_back(ver(v,w));
            edge[v].push_back(ver(u,w));
        }
        Dijkstra(1);
        printf("%d\n",ans[n]);
    }
    return 0;
}
  • B  -- HDU1224

  • Description

Weiwei is a software engineer of ShiningSoft. He has just excellently fulfilled a software project with his fellow workers. His boss is so satisfied with their job that he decide to provide them a free tour around the world. It's a good chance to relax themselves. To most of them, it's the first time to go abroad so they decide to make a collective tour.
The tour company shows them a new kind of tour circuit - DIY circuit. Each circuit contains some cities which can be selected by tourists themselves. According to the company's statistic, each city has its own interesting point. For instance, Paris has its interesting point of 90, New York has its interesting point of 70, ect. Not any two cities in the world have straight flight so the tour company provide a map to tell its tourists whether they can got a straight flight between any two cities on the map. In order to fly back, the company has made it impossible to make a circle-flight on the half way, using the cities on the map. That is, they marked each city on the map with one number, a city with higher number has no straight flight to a city with lower number.
Note: Weiwei always starts from Hangzhou(in this problem, we assume Hangzhou is always the first city and also the last city, so we mark Hangzhou both 1 and N+1), and its interesting point is always 0.
Now as the leader of the team, Weiwei wants to make a tour as interesting as possible. If you were Weiwei, how did you DIY it?

  • Input

The input will contain several cases. The first line is an integer T which suggests the number of cases. Then T cases follows.
Each case will begin with an integer N(2 ≤ N ≤ 100) which is the number of cities on the map
Then N integers follows, representing the interesting point list of the cities
And then it is an integer M followed by M pairs of integers [Ai, Bi] (1 ≤ i ≤ M). Each pair of [Ai, Bi] indicates that a straight flight is available from City Ai to City Bi

  • Output

For each case, your task is to output the maximal summation of interesting points Weiwei and his fellow workers can get through optimal DIYing and the optimal circuit. The format is as the sample. You may assume that there is only one optimal circuit
Output a blank line between two cases

  • Sample Input

2

3

0 70 90

4

1 2

1 3

2 4

3 4

3

0 90 70

4

1 2

1 3

2 4

3 4

  • Sample Output

CASE 1#

points : 90

circuit : 1->3->1

CASE 2#

points : 90

circuit : 1->2->1

  • 题目理解

因为题目已经给出了地图上点到n+1点的路径,所以直接求1-n+1的最短路就可以了,因为点数比较少许多算法都可以,题目也没有说明是否有负权边许多算法都可以接受

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=105;
int n,m;
int w[maxn][maxn],point[maxn],d[maxn],pre[maxn];
int inq[maxn],ans[maxn];
void init(){
    point[n]=0;
    memset(w,INF,sizeof(w));
    /*for(int i=0;i<=n;++i){
        for(int j=0;j<=n;++j){
            printf("%d ",w[i][j]);
        }printf("\n");
    }*/
}
void SPFA(int src){
    memset(inq,0,sizeof(inq));
    memset(pre,-1,sizeof(pre));
    memset(d,-1,sizeof(d));
    d[src]=0;
    queue<int>q;
    q.push(src);
    while(!q.empty()){
        int u=q.front();q.pop();
        inq[u]=0;
        for(int v=1;v<=n;++v)if(w[u][v]!=INF){
            int tmp=d[u]+w[u][v];
            if(d[v]<tmp){
                d[v]=tmp;
                pre[v]=u;
                if(!inq[v]){
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void print_path(int u){
    if(pre[u]==-1){
        printf("1");
        return;
    }
    print_path(pre[u]);
    if(u==n) printf("->%d",1);
    else printf("->%d",u);
}
int main(){
    int T,u,v,cas=1;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        ++n;
        init();
        for(int i=1;i<n;++i)
            scanf("%d",&point[i]);
        scanf("%d",&m);
        for(int i=0;i<m;++i){
            scanf("%d%d",&u,&v);
            w[u][v]=point[v];
        }
        SPFA(1);
        if(cas!=1)printf("\n");
        printf("CASE %d#\n",cas++);
        printf("points : %d\n",d[n]);
        printf("circuit : ");
        print_path(n);
        printf("\n");
    }
    return 0;
}
  • C -- HDU1384

  • Description

You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn.
Write a program that:
> reads the number of intervals, their endpoints and integers c1, ..., cn from the standard input,
> computes the minimal size of a set Z of integers which has at least ci common elements with interval [ai, bi], for each i = 1, 2, ..., n,
> writes the answer to the standard output

  • Input

The first line of the input contains an integer n (1 <= n <= 50 000) - the number of intervals. The following n lines describe the intervals. The i+1-th line of the input contains three integers ai, bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50 000 and 1 <= ci <= bi - ai + 1.
Process to the end of file

  • Output

The output contains exactly one integer equal to the minimal size of set Z sharing at least ci elements with interval [ai, bi], for each i = 1, 2, ..., n

  • Sample Input

5

3 7 3

8 10 3

6 8 1

1 3 1

10 11 1

  • Sample Output

6

  • 题目理解

很明显的差分约束但是不会做哈哈,这种题模板+会用模板很重要,求最小值的话如果标准化不等式为\geq最后求最长路,然后将区间的约束转化为d(v)\geq d(u-1)+w(u-1,v)建图由于可能有负权边SPFA松弛逼近最长路即可,如果需要判断是否矛盾的话计算一下入队列次数如果超过n就说明存在负权环

#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
int n,tot,S,T,u,v,w;
int dis[maxn],first[maxn],vis[maxn];
struct Node{
    int v,w,net;
} edge[maxn*10];

void addedge(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].net=first[u];
    first[u]=tot++;
}

void SPFA(int s)
{
    queue<int> q;
    memset(dis,-inf,sizeof dis);//最长路初始化为-无穷
    memset(vis,0,sizeof vis);
    q.push(s); dis[s]=0;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=first[u];~i;i=edge[i].net)
        {
            if(dis[edge[i].v]<dis[u]+edge[i].w)//最长路
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                if(!vis[edge[i].v])
                {
                    vis[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        tot=1, S=inf,T=-inf;
    	memset(first,-1,sizeof first);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            S=min(S,u-1),T=max(T,v);
            addedge(u-1,v,w);
        }
        for(int i=S;i<T;i++)
        {
            addedge(i,i+1,0);
            addedge(i+1,i,-1);
        }
        SPFA(S);
        printf("%d\n",dis[T]);
    }
    return 0;
}
  • D  -- HDU1599

  • Description

杭州有N个景区,景区之间有一些双向的路来连接,现在8600想找一条旅游路线,这个路线从A点出发并且最后回到A点,假设经过的路线为V1,V2,....VK,V1,那么必须满足K>2,就是说至除了出发点以外至少要经过2个其他不同的景区,而且不能重复经过同一个景区。现在8600需要你帮他找一条这样的路线,并且花费越少越好

  • Input

第一行是2个整数N和M(N <= 100, M <= 1000),代表景区的个数和道路的条数
接下来的M行里,每行包括3个整数a,b,c.代表a和b之间有一条通路,并且需要花费c元(c <= 100)

  • Output

对于每个测试实例,如果能找到这样一条路线的话,输出花费的最小值。如果找不到的话,输出"It's impossible."

  • Sample Input

3 3

1 2 1

2 3 1

1 3 1

3 3

1 2 1

1 2 3

2 3 1

  • Sample Output

3

It's impossible

  • 题目理解

为了保证成环并且需要最少三个点所以我们把一个环拆分成dist(i,j)+G(i,k)+G(j,k)并且dist(i,j)i-j的最短距离并且一定不包含k点,到时候把所有的k点遍历一遍就可以得到所有的可能,取最小值即可。由于i,j应该是不等于k的所有点,基于上面的定义没有什么好考虑的了\rightarrowfloyd算法.通过对邻接矩阵的k阶运算,并且限定插点顺序得到A^{k}为任意两点间的最短路径并且中间点一定不包含k点。如果我们要通过A^{k}A^{k+1}并且在这时候要将点k+1插入那么对任意i,j计算dist(i,j)=min(dist(i,j),dist(i,k)+dist(j,k))对应插入或者不插入的选择

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105;
const int INF=0x1f1f1f1f;
int G[maxn][maxn];
int dist[maxn][maxn];
int m,n,ans;
void floyd()
{
    ans=INF;
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(k!=i&&k!=j)
                ans=min(ans,dist[i][j]+G[i][k]+G[k][j]);//dist[i][j]里面必定不含K点
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                    dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);//计算得到k阶矩阵的值,这时候路径中的插点到达k
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(G,INF,sizeof(G));
        memset(dist,INF,sizeof(G));
        int s,e,w;
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&s,&e,&w);
            if(w<G[s][e])
            {
                G[s][e]=w=G[e][s]=w;
                dist[s][e]=w=dist[e][s]=w;
            }
        }
        floyd();
        if(ans<INF)
            printf("%d\n",ans);
        else
            printf("It's impossible.\n");
    }
    return 0;
}
  • K  -- 畅通工程

  • Description

某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰
现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离

  • Input

本题目包含多组数据,请处理到文件结束
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点

  • Output

对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1

  • Sample Input

3 3

0 1 1

0 2 3

1 2 1

0 2

3 1

0 1 1

1 2

  • Sample Output

2

-1

  • 题目理解

最短路标准使用Dijkstra即可,原理同A题

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct ver{
   int v,w;
   ver(){}
   ver(int V,int W):v(V),w(W){}
};
struct dist{
   int id,dis;
   dist(){};
   dist(int ID,int DIS):id(ID),dis(DIS){}
   friend bool operator<(const dist& a,const dist& b){
      return a.dis>b.dis;
   }
};
const int maxn=205;
const int inf=0x3f3f3f3f;
vector<ver> edge[maxn];
int ans[maxn];
void Dijkstra(int s){
   memset(ans,inf,sizeof(ans));
   priority_queue<dist> p;
   p.push(dist(s,0));
   ans[s]=0;
   while(!p.empty()){
     dist cur=p.top();p.pop();
     if(cur.dis!=ans[cur.id]) continue;
     for(int i=0;i<edge[cur.id].size();++i){
        int v=edge[cur.id][i].v;
        int w=edge[cur.id][i].w;
        if(ans[v]>cur.dis+w){
            ans[v]=cur.dis+w;
            p.push(dist(v,ans[v]));
        }
     }
   }
}
int main()
{
    int n,m,u,v,w,s,t;
    while(scanf("%d%d",&n,&m)!=EOF&&n+m){
        for(int i=0;i<maxn;++i) edge[i].clear();
        for(int i=0;i<m;++i){
            scanf("%d%d%d",&u,&v,&w);
            edge[u].push_back(ver(v,w));
            edge[v].push_back(ver(u,w));
        }
        scanf("%d%d",&s,&t);
        Dijkstra(s);
        if(ans[t]==inf) printf("%d\n",-1);
        else printf("%d\n",ans[t]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zm_zsy/article/details/81510597
今日推荐