2017暑假集训 div1 最短路(3)

POJ 1062

题意:中文题,略;

做法:虚拟一个起点为0节点(到其他物品的权值是原价),每个物品看成一个节点,酋长的允诺也看作一个物品, 如果一个物品加上金币可以交换另一个物品,则这两个节点之间有边,权值为优惠完后的值,求第0个节点到第1个节点的最短路

注意:因为有等级限制,所以枚举所能选的等级范围[l , r] , 多次最短路即可


#include <iostream>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <math.h>
using namespace std;
const int inf=0x3f3f3f;
int map[110][110];
int rate[110];
int n,m;
bool vis[110];
int dis[110];

int spfa(int l,int r)
{
    for(int i=0;i<=n;++i)
    {
        dis[i]=inf; vis[i]=0;
    }
    dis[0]=0; vis[0]=1;
    queue<int>que;
    que.push(0);
    while(!que.empty())
    {
        int u=que.front() ;que.pop(); vis[u]=0;
        for(int i=1;i<=n;++i)
        {
            if(rate[i]<l||rate[i]>r) continue;
            if(dis[i]>dis[u]+map[u][i])
            {
                dis[i]=dis[u]+map[u][i];
                if(!vis[i])
                {
                    vis[i]=1; que.push(i);
                }
            }
        }
    }
    return dis[1];
}

int main()
{
    scanf("%d%d",&m,&n);
    for(int i=0;i<=n;++i)
    {
        for(int j=0;j<=n;++j)
        {
            map[i][j]=inf;
            if(i==j) map[i][j]=0;
        }
    }
    rate[0]=inf;
    for(int i=1;i<=n;++i)
    {
        int p,l,x;
        scanf("%d%d%d",&p,&l,&x);
        rate[i]=l;

        map[0][i]=p;
        for(int j=1;j<=x;++j)
        {
            int t,v;
            scanf("%d%d",&t,&v);
            map[t][i]=max(0,min(v,map[t][i]));
        }
    }
    int ans=inf;
    for(int i=0;i<=m;++i)
    {
        int l=rate[1]+i-m;
        int r=rate[1]+i;
        ans=min(ans,spfa(l,r));
    }
    printf("%d\n",ans);
    return 0;
}



POJ 1847

题意:火车行驶过程有n条岔路(每次只能走一条),默认是第一条路..如果需要改路的话需要操作一次,问从a到b最少需要操作多少次n个点 a,b是起点和终点,然后n行,第i行有k个点,代表第i个点可以走到这k个点,初始默认方向是第一个点的方向...其他的改道一次需要操作一次。


做法:最短路,只需要把建边的时候,默认的方向为免费边,非默认方向权值为1,不能到达为inf

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
int n,a,b;
int map[110][110];
const int inf=0x3f3f3f;

bool vis[110];
int dis[110];
void spfa()
{
    for(int i=1;i<=n;++i)
    {
        vis[i]=0; dis[i]=inf;
    }
    vis[a]=1; dis[a]=0;
    queue<int>que;
    que.push(a);
    while(!que.empty())
    {
        int  u=que.front() ; que.pop(); vis[u]=0;
        for(int i=1;i<=n;++i)
        {
            if(dis[i]>dis[u]+map[u][i])
            {
                dis[i]=dis[u]+map[u][i];
                if(!vis[i])
                {
                    vis[i]=1; que.push(i);
                }
            }
        }
    }
    if(dis[b]==inf) dis[b]=-1;
    printf("%d\n",dis[b]);
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            map[i][j]=inf;
            if(i==j) map[i][j]=0;
        }
    }
    for(int i=1;i<=n;++i)
    {
        int m;
        scanf("%d",&m);
        int flag=0;
        for(int j=1;j<=m;++j)
        {
            int to;
            scanf("%d",&to);
            map[i][to]=flag;
            if(flag==0) flag=1;
        }
    }
    spfa();
    return 0;
}





LIGHT OJ 1074
题意:有n个城市,每一个城市有一个拥挤度ai,从一个城市I到另一个城市J的时间为:(aJ-aI)^3,存在负环。问从第一个城市到达第k个城市所花的时间,如果不能到达,或者时间小于3输出?否则输出所花的时间
思路:用spfa找出的所有的负环,把所有负环能到的点标记一下(这里用dfs,递归标记把负环里的点都标记了)。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m,k;;
int val[320];
int du[320];
int dis[320];
bool vis[320];
int cic[320];
int head[320];
int cnt=0;
struct node
{
    int to,next,w;
}edge[200*410];

void add(int u,int v,int w)
{
    edge[cnt].to=v; edge[cnt].w=w;
    edge[cnt].next=head[u]; head[u]=cnt++;
}

void dfs(int x)
{
   cic[x]=1;
   for(int i=head[x];i!=-1;i=edge[i].next)
   {
       int v=edge[i].to;
       if(!cic[v]) dfs(v);
   }
}
void spfa(int x)
{
    queue<int> que;
    while(que.size()) que.pop();
    memset(vis,0,sizeof(vis));
    memset(du,0,sizeof(du));
    memset(cic,0,sizeof(cic));
    memset(dis,inf,sizeof(dis));

    dis[x]=0; vis[x]=1; du[x]++;  que.push(x);

    while(!que.empty())
    {
        int u=que.front() ;que.pop(); vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to, w=edge[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v]&&!cic[v])
                {
                    vis[v]=1; que.push(v); du[v]++;
                }
                if(du[v]>=n&&!cic[v]) dfs(v);
            }
        }
    }
}


int main()
{
    int T;
    cin>>T;
    int kase=1;
    while(T--)
    {
        memset(head,-1,sizeof(head)); cnt=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&val[i]);
        }
        scanf("%d",&m);
        while(m--)
        {
            int c1,c2;
            scanf("%d%d",&c1,&c2); int temp=val[c2]-val[c1];
            add(c1,c2,temp*temp*temp);
        }

        spfa(1);
        scanf("%d",&k);
        printf("Case %d:\n",kase++);
        while(k--)
        {
            int aim;
            scanf("%d",&aim);
            if(cic[aim]||dis[aim]<3||dis[aim]==inf) printf("?\n");
            else printf("%d\n",dis[aim]);
        }
    }
    return 0;
}


HDU 4725
题意:n个点,m条边,以及相邻层之间移动的代价c,给出每个点所在的层数,以及m条边,每条边有u,v,c,表示从节点u到v(无向),并且移动的代价 c ,问说从 1 到 n 的代价最小是多少。

做法:拆点,1-N代表点 N+1-2*N 代表层数, 建图一定要小心,自己建图的时候忘记判断两层是否有点,wa了好几发。
需要建的边,  该层内的点 到 代表该层的点                                                 权值为0   
                        代表不同层的点之间                                                             权值为c  (记得判断层内是否有点)
                        点 到 代表非本层的点(只用取左右两层即可)                    权值为c  (记得判断1[无左]和n层[无右]) 
                        



代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
typedef long long ll;
using namespace std;
const long long inf=0x3f3f3f3f;
const int maxn=200000+10;
const int mam=800000+10;
int head[maxn];
int lay[maxn];
bool vislay[maxn];
int cnt=0;
int n,m;
int c;
struct node
{
    int next,to;
    int w;
}edge[maxn*50];
void add(int u,int v,ll w)
{
    edge[cnt].next=head[u]; edge[cnt].to=v;
    edge[cnt].w=w; head[u]=cnt++;
}

bool vis[maxn];
int dis[maxn];
struct enode
{
    int pos,step;
    bool friend operator < (const enode a, const enode b)
    {
        return a.step>b.step;
    }
};
void dij()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=2*n;++i) dis[i]=inf;
    dis[1]=0;
    struct enode u,v;
    u.step=0;u.pos=1;
    priority_queue<enode> que;
    que.push(u);
    while(!que.empty())
    {
         u=que.top() ;que.pop();
         if(vis[u.pos])  continue;
         vis[u.pos]=1;
         int st=u.pos;
         for(int i=head[st];i!=-1;i=edge[i].next)
         {
             int to=edge[i].to;
             int w=edge[i].w;
             if(dis[to]>dis[st]+w)
             {
                 dis[to]=dis[st]+w;
                 if(!vis[to])
                 {
                     v.pos=to; v.step=dis[to];
                     que.push(v);
                 }
             }
         }
   }
   if(dis[n]==inf||n==0) dis[n]=-1;
   printf("%d\n",dis[n]);
}
int main()
{
    int T;
    scanf("%d",&T);
    int kcase=1;
    while(T--)
    {
        memset(head,-1,sizeof(head)); cnt=0;
        memset(vislay,0,sizeof(vislay));
        scanf("%d%d%d",&n,&m,&c);
        for(int i=1;i<=n;++i)
        {
            int temp;
            scanf("%d",&temp);
            lay[i]=temp;    vislay[temp]=1;
        }
        for(int i=1;i<n;++i)
        {
           if(vislay[i]&&vislay[i+1])
           {
               add(i+n,i+1+n,c);  add(i+n+1,i+n,c);
           }
        }
        for(int i=1;i<=n;++i)
        {
            add(n+lay[i],i,0);
            if(lay[i]>1)
            {
                add(i,n+lay[i]-1,c);  add(n+lay[i]-1,i,c);
            }
            if(lay[i]<n)
            {
                add(i,n+lay[i]+1,c); add(n+lay[i]+1,i,c);
            }
        }

        for(int i=1;i<=m;++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w); add(v,u,w);
        }
        printf("Case #%d: ",kcase++);
        dij();
    }
    return 0;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值