zoukankan      html  css  js  c++  java
  • 最短路

    今天决定刷图论了,做到至少比赛的时候图论的题能够写出来。。。

    POJ-1062-昂贵的聘礼

    Dijkstra即可。。

    因为这题可以抽象出边上有权值,点上也有权值。。我们可以先忽略点上的权值,用Dijkstra算出1~n的最短路,最后再加上点上的权值,然后求所有点的最小值即可。。否则把点上的权值也带入求最小值,会wa。。。我被wa了N次了。。。

    这题有等级制度,故需要枚举区间进行多次最短路。

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MAXN 105
    #define MM 100000000
    using namespace std;
    
    int g[MAXN][MAXN];
    int v[MAXN];
    int d[MAXN];
    int vis[MAXN];
    int rank[MAXN];
    
    int dis(int n,int m,int a,int b)
    {
        int ans=v[1];
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            d[i]=MM;
        d[1]=0;
        for(int i=1;i<=n;i++)
        {
            if(g[1][i]&&rank[i]<=b&&rank[i]>=a)
            {
                d[i]=g[1][i];
            }
        }
        vis[1]=1;
        for(int i=1;i<=n;i++)
        {
            int minn=MM;
            int k=0;
            for(int j=1;j<=n;j++)
            {
                if(d[j]<minn&&!vis[j])
                {
                    minn=d[j];
                    k=j;
                }
            }
            vis[k]=1;
            if(k==0)
                break;
        //        return ans;
        //    ans=min(ans,d[k]);
            for(int j=1;j<=n;j++)
            {
                if(g[k][j]&&!vis[j]&&rank[j]<=b&&rank[j]>=a&&d[k]+g[k][j]<d[j])
                    d[j]=d[k]+g[k][j];
            }
        }
        for(int i=1;i<=n;i++)
            ans=min(ans,d[i]+v[i]);
        return ans;
    }
    int main(){
        int n,m;
        while(scanf("%d%d",&m,&n)==2)
        {
            memset(g,0,sizeof(g));
            int k;
            for(int i=1;i<=n;i++)
            {
                scanf("%d%d%d",&v[i],&rank[i],&k);
                for(int j=1;j<=k;j++)
                {
                    int a,b;
                    scanf("%d%d",&a,&b);
                    g[i][a]=b;
                }
            }
            int ans=v[1];
            for(int i=rank[1]-m;i<=rank[1];i++)//枚举区间
            {
                ans=min(dis(n,m,i,i+m),ans);
            }
            printf("%d\n",ans);
        }
    
        return 0;
    }

    POJ-1502 MPI Maelstrom

    这题是求所有最短路中的最大值。。用dijkstra即可

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MM 1000000000
    #define MAXN 105
    using namespace std;
    int g[MAXN][MAXN];
    int d[MAXN],vis[MAXN];
    
    int dis(int s,int n)
    {
        int ans=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            d[i]=g[1][i];
        }
        vis[1]=1;
        for(int i=1;i<=n;i++)
        {
            int minc=MM,k=0;
            for(int j=1;j<=n;j++)
            {
                if(d[j]<minc&&!vis[j])
                {
                    minc=d[j];k=j;
                }
            }
            if(k==0)
            //    return ans;
            break;
            vis[k]=1;
            //ans+=d[k];
            ans=max(ans,minc);
            for(int j=1;j<=n;j++)
            {
                if(g[k][j]+d[k]<d[j]&&!vis[j])
                    d[j]=g[k][j]+d[k];
            }
        //    return ans;
        }
    
        return ans;
    }
    
    int main()
    {
        int n;
        scanf("%d",&n);
        char ch[100];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i==j)
                    g[i][j]=0;
                else
                    g[i][j]=MM;
        for(int i=2;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                scanf("%s",ch);
                if(ch[0]=='x')
                    continue;
                int s=0;
                int len=strlen(ch);
                for(int k=0;k<len;k++)
                    s=s*10+ch[k]-'0';
            //    scanf("%d",&g[i][j]);
                g[j][i]=g[i][j]=s;
            //    printf("%d %d %d\n",i,j,s);
            }
        }
        printf("%d\n",dis(1,n));
    }

     POJ-2387-Til the Cows Come Home

    这题目要注意重边。。。dij算法要考虑重边

    啊啊啊,以后做图论题得先把特殊情况都先考虑好啊啊。。

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MAXN 1005
    #define INF 1000000000
    using namespace std;
    int g[MAXN][MAXN];
    int vis[MAXN],d[MAXN];
    int T,N;
    int dij(int s,int end)
    {
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=N;i++)
            if(g[s][i])
                d[i]=g[s][i];
            else d[i]=INF;
            d[s]=0;
        for(int i=1;i<=N;i++)
        {
            int minc=INF,k=0;
            for(int j=1;j<=N;j++)
                if(d[j]<minc&&!vis[j])
                {
                    minc=d[j];k=j;
                }
            if(k==0)
                break;
            vis[k]=1;
            if(k==end)
                return d[k];
            for(int j=1;j<=N;j++)
                if(!vis[j]&&d[j]>d[k]+g[k][j]&&g[k][j])
                    d[j]=d[k]+g[k][j];
        }
        return d[end];
    }
    int main()
    {
    
        scanf("%d%d",&T,&N);
        int a,b,c;
        memset(g,0,sizeof(g));
        for(int i=1;i<=T;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(g[a][b])
                g[a][b]=g[b][a]=min(g[a][b],c);
            else
            g[a][b]=g[b][a]=c;
        }
        printf("%d\n",dij(N,1));
    }

    POJ-3268-Silver Cow Party

    这题还好,挺好做的,只是需要转换一点罢了,用dij

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define INF 1000000000
    #define MAXN 1005
    using namespace std;
    int g[MAXN][MAXN];
    int N,M,X;
    int gg[MAXN][MAXN];
    int vis[MAXN],d[MAXN];
    int ans[MAXN];
    void dis(int s,int m[MAXN][MAXN])
    {
        for(int i=1;i<=N;i++)
            if(m[s][i])
            d[i]=m[s][i];
            else d[i]=INF;
        d[s]=0;
        memset(vis,0,sizeof(vis));
        vis[s]=1;
        for(int i=1;i<=N;i++)
        {
            int minc=INF,k=0;
            for(int j=1;j<=N;j++)
            {
                if(!vis[j]&&d[j]<minc)
                {
                    minc=d[j];k=j;
                }
            }
            if(k==0)
                break;
            vis[k]=1;
            for(int j=1;j<=N;j++)
            {
                if(!vis[j]&&d[j]>d[k]+m[k][j]&&m[k][j])
                    d[j]=d[k]+m[k][j];
            }
        }
        for(int i=1;i<=N;i++)
            ans[i]+=d[i];
    }
    int main()
    {
        int a,b,t;
        scanf("%d%d%d",&N,&M,&X);
        memset(g,0,sizeof(g));
        memset(gg,0,sizeof(gg));
        memset(ans,0,sizeof(ans));
        for(int i=1;i<=M;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            g[a][b]=gg[b][a]=t;
        }
        dis(X,g);
        dis(X,gg);
        int maxn=0;
        for(int i=1;i<=N;i++)
            maxn=max(ans[i],maxn);
        printf("%d\n",maxn);
    }

     POJ-1511-Invitation Cards

    题意:在一个城市里,有n(1~1000000)个运输中心,有m条有向路连接着任意的两个运输中心。ACM组织(位于编号为1的运输中心)要派发p个雇佣员工早上前往这p个运输中心去发传单,晚上再让他们都回到组织中。问所有人所走路程的总和最短是多少?

    因为是稀疏图,所以用邻接表,所以用spfa算法。输入的时候存两个图,一个是原图,一个是原图的每条边都反向的图。。这样求两次spfa从一点到其他所有点的距离即可。

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MAXN 1000050
    #define INF 1000000000
    
    using namespace std;
    
    struct node{
        int v,w,next;
    }edge1[MAXN];
    node edge2[MAXN];
    int first1[MAXN],first2[MAXN];
    long long d[MAXN];
    int q[MAXN],vis[MAXN];
    int P,Q;
    int e1,e2;
    void init(int n)
    {
        memset(first1,-1,sizeof(first1));
        memset(first2,-1,sizeof(first2));
        e1=0,e2=0;
    }
    void add1(int u,int v,int w)
    {
        edge1[e1].v=v;
        edge1[e1].w=w;
        edge1[e1].next=first1[u];
        first1[u]=e1++;
    }
    void add2(int u,int v,int w)
    {
        edge2[e2].v=v;
        edge2[e2].w=w;
        edge2[e2].next=first2[u];
        first2[u]=e2++;
    }
    long long spfa(int s,node *edge,int *first)
    {
        long long ans=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=P;i++)
            d[i]=INF;
        d[s]=0;
        int rear=0,front=0;
        q[rear++]=s;
        while(rear!=front)
        {
            int u=q[front++];
            if(front>P)
                front=0;
            vis[u]=0;
            for(int i=first[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].v;
                if(d[v]>d[u]+edge[i].w)
                {
                    d[v]=d[u]+edge[i].w;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q[rear++]=v;
                        if(rear>P)
                            rear=0;
                    }
                }
            }
        }
        for(int i=1;i<=P;i++)
            ans+=d[i];
        return ans;
    }
    int main()
    {
        int tt;
        scanf("%d",&tt);
        while(tt--)
        {
            int a,b,c;
            scanf("%d%d",&P,&Q);
            init(P);
            for(int i=1;i<=Q;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                add1(a,b,c);
                add2(b,a,c);
            }
            long long ans=0;
            ans+=spfa(1,edge1,first1);
            ans+=spfa(1,edge2,first2);
            cout<<ans<<endl;
        }
    }

    POJ-1724-ROADS

    题意:Bob现在有的钱数为coin,他想从城市1到城市n,给出m条连接两个城市的有向边,并且给出路的长度w,和经过这条路要交的钱数c。问Bol能从1到达城市n的最短路径为多长。(总共扣的钱数必须不大于coin)

    分析:改普通的dij为:只要一边起点的当前花费钱数 + 这条边的花费钱数 <= coin,就让这边终点的信息加入优先队列.(普通的dij是终边的距离有变小才让其加入优先队列)

    这题的dij还用到了邻接表。其实这完全可以看做BFS+优先队列

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    
    #define MAXN 10050
    using namespace std;
    struct ee{
        int v,w,c,next;
    }edge[MAXN];
    struct node{
        int u,l,c;
        bool operator <(const node &a)const{
            return l>a.l;
        };
    };
    int first[105];
    priority_queue<node> q;
    int K,N,R;
    int e;
    
    void init(int n)
    {
        memset(first,-1,sizeof(first));
        e=0;
    }
    void add(int u,int v,int w,int c)
    {
        edge[e].v=v;
        edge[e].w=w;
        edge[e].c=c;
        edge[e].next=first[u];
        first[u]=e++;
    }
    int dij(int s,int end)
    {
        while(!q.empty())
        {
            q.pop();
        }
        node a;
        a.u=s,a.l=0,a.c=0;
        q.push(a);
        while(!q.empty())
        {
            node now=q.top();
            q.pop();
            int u=now.u;
            if(u==end)return now.l;
            for(int i=first[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].v;
                if(now.c+edge[i].c<=K)
                {
                    a.u=v,a.l=now.l+edge[i].w,a.c=now.c+edge[i].c;
                    q.push(a);
                }
                    
            }
        }
        return -1;
    }
    int main()
    {
        scanf("%d%d%d",&K,&N,&R);
        int s,d,l,t;
        init(N);
        for(int i=0;i<R;i++)
        {
            scanf("%d%d%d%d",&s,&d,&l,&t);
            add(s,d,l,t);
        }
        printf("%d\n",dij(1,N));
    }

     POJ-1797-Heavy Transportation

    题意:求1到n路径权值最小值中的最大值

    用变形的dij写,其实这个我之前都是用变形的spfa写的,dij不会写,变形的地方原理一样。。

        for(int j=1;j<=n;j++)
                if(!vis[j]&&d[j]<min(d[k],g[k][j]))
                    d[j]=min(d[k],g[k][j]);

    好吧,贴上变形的dij代码

    View Code
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    
    #define MAXN 1005
    #define INF 1000000000
    using namespace std;
    int g[MAXN][MAXN];
    int d[MAXN],vis[MAXN];
    int n,m;
    
    int dij(int s,int end)
    {
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
                d[i]=g[s][i];
        vis[s]=1,d[s]=0;
        for(int i=1;i<=n;i++)
        {
            int maxn=0,k=0;
            for(int j=1;j<=n;j++)
                if(d[j]>maxn&&!vis[j])
                {
                    maxn=d[j];k=j;
                }
            if(!k)
                break;
            vis[k]=1;
            for(int j=1;j<=n;j++)
                if(!vis[j]&&d[j]<min(d[k],g[k][j]))
                    d[j]=min(d[k],g[k][j]);
        }
        return d[n];
    }
    int main()
    {
        int tt;
        scanf("%d",&tt);
        for(int cas=1;cas<=tt;cas++)
        {
            memset(g,0,sizeof(g));
            scanf("%d%d",&n,&m);
            int a,b,c;
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                if(g[a][b])
                    g[a][b]=g[b][a]=max(c,g[a][b]);
                else g[a][b]=g[b][a]=c;
            }
            printf("Scenario #%d:\n",cas);
            printf("%d\n\n",dij(1,n));
        }
    }

     POJ-2253-Frogger

    题意:

    给出两只青蛙的坐标A、B,和其他的n-2个坐标,任一两个坐标点间都是双向连通的。显然从A到B存在至少一条的通路,每一条通路的元素都是这条通路中前后两个点的距离,这些距离中又有一个最大距离。

    现在要求求出所有通路的最大距离,并把这些最大距离作比较,把最小的一个最大距离作为青蛙的最小跳远距离。

    变形的dij

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #define MAXN 205
    using namespace std;
    double g[MAXN][MAXN];
    struct node{
        int x,y;
    }p[MAXN];
    double d[MAXN];
    int vis[MAXN];
    int n;
    double cul(node a,node b){
        return sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
    }
    double dij(int s,int end){
        for(int i=1;i<=n;i++)
        d[i]=g[s][i];
        memset(vis,0,sizeof(vis));
        vis[s]=1;
        for(int i=1;i<=n;i++){
            double minc=1000000000;
            int k=0;
            for(int j=1;j<=n;j++){
                if(minc>d[j]&&!vis[j])
                {
                    minc=d[j],k=j;
                }
            }
            if(k==0)break;
            vis[k]=1;
            for(int j=1;j<=n;j++){
                if(!vis[j]&&(d[j]>max(d[k],g[k][j])))
                d[j]=max(d[k],g[k][j]);
            }
        }
        return d[end];
    }
    int main()
    {
        int cas=1;
        while(scanf("%d",&n)==1){
            if(!n)break;
            int a,b;
            for(int i=1;i<=n;i++){
                scanf("%d%d",&a,&b);
                p[i].x=a,p[i].y=b;
            }
            for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            g[i][j]=1000000000;
            for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            g[i][j]=cul(p[i],p[j]);
            printf("Scenario #%d\n",cas++);
            printf("Frog Distance = %.3lf\n\n",dij(1,2));
        }
        return 0;
    }

     POJ-2502-Subway

    分析:就是求两点之间的最短距离,不过这题的建图麻烦点。。dij水之

    View Code
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    #define MAXN 205
    using namespace std;
    double g[MAXN][MAXN];
    bool f[MAXN][MAXN];
    struct node{
        int x,y;
    }p[MAXN];
    int n;
    double walkv,subv;
    double d[MAXN];
    int vis[MAXN];
    double dis(node a,node b)
    {
        return sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
    }
    double dij(int s,int end)
    {
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)
            d[i]=g[s][i];
        vis[s]=1;
        for(int i=0;i<n;i++)
        {
            double minc=1000000000;
            int k=-1;
            for(int j=0;j<n;j++)
                if(!vis[j]&&minc>d[j])
                {
                    k=j,minc=d[j];
                }
            if(k==-1)break;
            if(k==end)
                return d[k];
            vis[k]=1;
            for(int j=0;j<n;j++)
                if(!vis[j]&&d[k]+g[k][j]<d[j])
                    d[j]=d[k]+g[k][j];
        }
        return d[end];
    }
    int main()
    {
        scanf("%d%d",&p[0].x,&p[0].y);
        scanf("%d%d",&p[1].x,&p[1].y);
        n=2;
        int a,b;
        int m=2;
        subv=40*1000.0/60;
        walkv=10*1000.0/60;
        memset(f,false,sizeof(f));
        while(scanf("%d%d",&a,&b)==2)
        {
            if(a==-1&&b==-1)
            {
                for(int i=m;i<n-1;i++)
                {
                    g[i][i+1]=g[i+1][i]=dis(p[i],p[i+1])/subv;
                    f[i][i+1]=f[i+1][i]=true;
                }
                m=n;
                continue;
            }
            p[n].x=a,p[n].y=b;
            n++;
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                if(!f[i][j])
                    g[i][j]=dis(p[i],p[j])/walkv;
        printf("%d\n",(int)(dij(0,1)+0.5));
    }

     POJ-3013-Big Christmas Tree

    分析:每条边的权值=这条边的unit price*这条边的所有子孙节点的和,求一棵树n个节点的(n-1)条边边权之和的最小值。

    可以转化为ans = ∑dist[i] * weight[i].(1 <= i <= vn),其中dist[i]为结点i到根结点root(1)的距离,vn为结点数.

    这题还有空树的情况。。伤不起啊。。

    用SPFA即可

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define MAXN 50050
    #define LL long long
    #define INF 10000000000LL
    using namespace std;
    struct ee{
        int v,w,next;
    }edge[MAXN*3];
    int first[MAXN];
    int e;
    LL d[MAXN];
    int vis[MAXN],q[MAXN];
    int cnt[MAXN];
    int p[MAXN];
    int n,m;
    void add(int u,int v,int w){
        edge[e].v=v;
        edge[e].w=w;
        edge[e].next=first[u];
        first[u]=e++;
    }
    LL spfa(int s){
        int front=0,rear=0;
        memset(vis,0,sizeof(vis));
    //    memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
        d[i]=INF;
        q[rear++]=s;
        d[s]=0;
        vis[s]=1;
        while(front!=rear){
            int u=q[front++];
            if(front>n)front=0;
            vis[u]=0;
            for(int i=first[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                if(d[v]>d[u]+edge[i].w){
                    d[v]=d[u]+edge[i].w;
                    if(!vis[v]){
                        q[rear++]=v;
                //        cnt[v]++;
               //         if(cnt[v]>n)return -1;//这个用来求是否有负环,有,返回-1
                        if(rear>n)rear=0;
                        vis[v]=1;
                    }
                }
            }
        }
        LL ans=0;
        for(int i=1;i<=n;i++)
        if(d[i]==INF)return -1;
        else
        ans+=d[i]*p[i];
        return ans;
    }
    int main()
    {
        freopen("in.txt","r",stdin);
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)
            scanf("%d",&p[i]);
            int a,b,c;
            memset(first,-1,sizeof(first));
            e=0;
            for(int i=0;i<m;i++){
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);add(b,a,c);
            }
            if(n==0)printf("0\n");
            else{
            LL ans=spfa(1);
            if(ans==-1)printf("No Answer\n");
            else
            printf("%I64d\n",ans);
            }
        }
        return 0;
    }

     POJ-3635-Full Tank?

    题意:坐汽车旅游,有n个加油站,每个站有自己的油价,假设每个单位距离耗一个单位油。车子邮箱最多能装c单位油。

    问从a到b的最小花费

    分析:这题要用到DP状态转移的思想,dp[i][j]表示在i站剩余j单位油时的最小花费,增加状态要一点一点的增加,如果把第i个站的油量1-cap一次性增加,会超时,增加了很多不需要的状态。。

    算法:变形的dij

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define MAXN 1050
    #define INF 1000000000
    using namespace std;
    struct ee{
        int v,w,next;
    }edge[MAXN*10*2];
    int e,first[MAXN];
    int dp[MAXN][105];//在i位置剩余j单位油时最少花费
    int vis[MAXN][105];
    struct node{
        int city;//当前城市
        int fuel;//当前油剩余量
        int cost;//当前花费
        bool operator <(const node &a)const{
            return cost>a.cost;
        };
    };
    int p[MAXN];
    priority_queue<node>q;
    int n,m;
    void add(int u,int v,int w){
        edge[e].v=v;
        edge[e].w=w;
        edge[e].next=first[u];
        first[u]=e++;
    }
    int dij(int s,int end,int cap){
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)
        for(int j=0;j<=cap;j++)dp[i][j]=INF;
    
        while(!q.empty())q.pop();
        node a;
        a.city=s,a.cost=0,a.fuel=0;
        q.push(a);
        dp[s][0]=0;
        vis[s][0]=1;
        while(!q.empty()){
            node now=q.top();
            q.pop();
            int u=now.city;
            if(u==end)return now.cost;
      /*      for(int i=now.fuel+1;i<=cap&&!vis[u][i]&&dp[u][i]>dp[u][i-1]+p[u];i++)
            {
                a.city=now.city;
                a.cost=dp[u][i-1]+p[u];
                dp[u][i]=dp[u][i-1]+p[u];
                a.fuel=i;
                q.push(a);
            }
            */
            int i=now.fuel+1;//只需将油量增一即可,而不是全加上,如上注释代码,全加上会超时,因为增加了许多不需要的状态
            if(i<=cap&&!vis[u][i]&&dp[u][i]>dp[u][i-1]+p[u]){
                a.city=now.city;
                a.cost=dp[u][i-1]+p[u];
                dp[u][i]=dp[u][i-1]+p[u];
                a.fuel=i;
                q.push(a);
            }
            for(i=first[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                int k=now.fuel-edge[i].w;
                if(k>=0&&!vis[v][k]&&dp[v][k]>now.cost){
                    dp[v][k]=now.cost;
                    a.city=v;
                    a.cost=now.cost;
                    a.fuel=k;
                    q.push(a);
                }
            }
        }
        return -1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        scanf("%d",&p[i]);
        int a,b,c;
        memset(first,-1,sizeof(first));
        e=0;
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);add(b,a,c);
        }
        int tt;
        scanf("%d",&tt);
        while(tt--){
            scanf("%d%d%d",&a,&b,&c);
            int ans=dij(b,c,a);
            if(ans==-1)printf("impossible\n");
            else printf("%d\n",ans);
        }
        return 0;
    }

     POJ-3621-Sightseeing Cows

    http://blog.sina.com.cn/s/blog_69a101130100jwci.html这个解题报告很好

    这题要用c++,用G++会wa

    题意:求一个环,环中每点都有一个fun值,还有路上所花时间,求环中所有fun值和/遍历环的时间的最大值

    分析:通过二分答案,判断负环,求得答案

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #define MAXN 5050
    #define INF 1000000000
    using namespace std;
    struct ee{
        int v,w,next;
    }edge[MAXN];
    int p[MAXN];
    int first[MAXN];
    int e;
    double d[MAXN];
    int vis[MAXN],q[MAXN],cnt[MAXN];
    int L,P;
    void add(int u,int v,int w){
        edge[e].v=v;
        edge[e].w=w;
        edge[e].next=first[u];
        first[u]=e++;
    }
    bool spfa(double ans){
        int front=0,rear=0;
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=L;i++)
        d[i]=INF;
        d[1]=0;
        q[rear++]=1;
        vis[1]=1;
        while(rear!=front){
            int u=q[front++];
            if(front>L)front=0;
            vis[u]=0;
            for(int i=first[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                if(d[v]>d[u]+edge[i].w*ans-p[v]){
                    d[v]=d[u]+edge[i].w*ans-p[v];
                    if(!vis[v]){
                        q[rear++]=v;
                        if(rear>L)rear=0;
                        vis[v]=1;
                        cnt[v]++;
                        if(cnt[v]>L)return false;
                    }
                }
            }
        }
        return true;
    }
    int main()
    {
        scanf("%d%d",&L,&P);
        for(int i=1;i<=L;i++)
        scanf("%d",&p[i]);
        int a,b,c;
        memset(first,-1,sizeof(first));
        e=0;
        for(int i=0;i<P;i++){
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
        }
        double l=0,r=1000;
        double ans,mid;
        while(l<=r){
            ans=mid;
            mid=(l+r)/2;
            if(fabs(ans-mid)<1e-6)
            break;
            if(spfa(mid)){
                r=mid;
            }
            else
            l=mid;
        }
        printf("%.2lf\n",ans);
        return 0;
    }

     POJ-3037-Skiing

    题意:一个矩形,求左上角到右下角花费的最小时间。。矩形上的点的值代表高度,

    已知初始速度v,从a 点到 b 点时,速度变为v(a)*2^(A-B)(A,B为对应点的高度),从 a 到 b 所需的时间为 a 的速度的倒数

     ,故速度就用倒数表示,,,

    View Code
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #define MAXN 105
    using namespace std;
    int g[MAXN][MAXN];
    bool vis[MAXN][MAXN];
    int V,R,C;
    int x[4]={-1,1,0,0};
    int y[4]={0,0,1,-1};
    struct point{
        int x,y;
        double v,t;
        bool operator < (const point & a)const{
            return t>a.t;
        }
    };
    priority_queue<point> q;
    
    double vvv(int x){
        double t=1.0;
        if(x>=0){
            while(x--){
                t=t*2;
            }
        }
        else{
            while(x++){
                t=t/2;
            }
        }
        return t;
    }
    double BFS(){
        memset(vis,false,sizeof(vis));
        point a;
        a.x=0;a.y=0;a.v=1.0/V;//速度用倒数表示好计算时间
        a.t=0;
        while(!q.empty()){
            q.pop();
        }
        q.push(a);
        while(!q.empty()){
            point newp;
            newp=q.top();
            q.pop();
            if(vis[newp.x][newp.y])continue;
            vis[newp.x][newp.y]=true;
            if(newp.x==R-1&&newp.y==C-1)
            return newp.t;
            for(int i=0;i<4;i++){
                int xx=newp.x+x[i];
                int yy=newp.y+y[i];
                if(xx>=0&&xx<R&&yy>=0&&yy<C){
                    if(!vis[xx][yy]){
                        a.x=xx,a.y=yy;
                        a.t=newp.v+newp.t;
                        a.v=newp.v*vvv(g[a.x][a.y]-g[newp.x][newp.y]);
                        q.push(a);
                    }
                }
            }
        }
        return -1;
    }
    int main()
    {
        freopen("in.txt","r",stdin);
        while(~scanf("%d%d%d",&V,&R,&C)){
        for(int i=0;i<R;i++)
        for(int j=0;j<C;j++){
            scanf("%d",&g[i][j]);
        }
        printf("%.2lf\n",BFS());
        }
        return 0;
    }

     POJ-3159-Candies

    题意:求1与N的相差的最大值

    分析:查分约束,a+c>=b则a--->b的路径的权值为c,这么建图就可以像求最短路径那样写。。。最后d[N]即是结果;

    这题要用spfa+栈来写,用spfa+队列会超时。。。也可以用dij+优先队列

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define MAXM 150050
    #define MAXN 30050
    #define INF 1000000000
    using namespace std;
    struct eee{
        int w,v,next;
    }edge[MAXM];
    int first[MAXN],d[MAXN],q[MAXN],vis[MAXN];
    int e;
    int N,M;
    void add(int u,int v,int w){
        edge[e].v=v;
        edge[e].w=w;
        edge[e].next=first[u];
        first[u]=e++;
    }
    void init(){
        memset(first,-1,sizeof(first));
        e=0;
    }
    int spfa(){
        int top=1;
        memset(vis,0,sizeof(vis));
        for(int i=0;i<=N;i++)d[i]=INF;
        d[1]=0;
        q[top]=1;
        while(top){
            int u=q[top--];
            vis[u]=0;
            for(int i=first[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                if(d[v]>d[u]+edge[i].w){
                    d[v]=d[u]+edge[i].w;
                    if(!vis[v]){
                        q[++top]=v;
                        vis[v]=1;
                    }
                }
            }
        }
        return d[N];
    }
    int main()
    {
        while(scanf("%d%d",&N,&M)==2){
            int a,b,c;
            init();
            for(int i=0;i<M;i++){
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
            }
            printf("%d\n",spfa());
        }
        return 0;
    }

     POJ-3615-Cow Hurdles

    题意:用floyd求两点之间的最大值中的最小值

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define MAXN 350
    #define INF 1000000000
    using namespace std;
    int g[MAXN][MAXN];
    int N,M,T;
    void floyd(){
    
        for(int k=1;k<=N;k++)
        for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++){
            if(g[i][j]>max(g[i][k],g[k][j])){
                g[i][j]=max(g[i][k],g[k][j]);
            }
        }
    }
    int main()
    {
        scanf("%d%d%d",&N,&M,&T);
        int a,b,c;
        for(int i=0;i<=N;i++)
        for(int j=0;j<=N;j++)
        g[i][j]=INF;
        for(int i=0;i<M;i++){
            scanf("%d%d%d",&a,&b,&c);
            g[a][b]=c;
        }
        floyd();
        for(int i=0;i<T;i++){
            scanf("%d%d",&a,&b);
            if(g[a][b]==INF)printf("-1\n");
            else printf("%d\n",g[a][b]);
        }
        return 0;
    }

     POJ-3660-Cow Contest

    题解引用Rainy Days

    题意:牛之间有绝对的强弱,给出一些胜负关系,问有多少头牛可以确定其绝对排名。

    分析:有人说叫闭包传递。这题就是用一种类似于floyd的算法,开始时,如果a胜b则由a到b连一条边。这样所有a能走到的点都是排名在a以后的。所有能走到a的点都是排名在a以前的。用floyd,求出每个点能到达的点。如果有一个点,排名在它之前的和排名在它之后的点之和为n-1,那么它的排名就是确定的。

    View Code
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define MAXN 105
    using namespace std;
    int g[MAXN][MAXN];
    int N,M;
    int floyd(){
        for(int k=1;k<=N;k++)
        for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++){
            if(g[i][k]&&g[k][j])
            g[i][j]=1;
        }
        int ans=0;
        for(int i=1;i<=N;i++){
            int cnt=0;
            for(int j=1;j<=N;j++){
                cnt+=g[i][j]+g[j][i];
            }
            if(cnt==N-1)ans++;
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d",&N,&M);
        memset(g,0,sizeof(g));
        int a,b;
        for(int i=0;i<M;i++){
            scanf("%d%d",&a,&b);
            g[a][b]=1;
        }
        printf("%d\n",floyd());
        return 0;
    }
    • 占位未完待续
  • 相关阅读:
    Python学习笔记009_构造与析构
    Python学习笔记008_类_对象_继承_组合_类相关的BIF
    Python学习笔记007_图形用户界面[EasyGui][Tkinter]
    Python学习笔记006_异常_else_with
    Python学习笔记005_文件_OS_模块_pickle
    Python学习笔记004_字典_集合
    小甲鱼:Python学习笔记003_函数
    小甲鱼:Python学习笔记002_数组_元组_字符串
    Java数据库连接泄漏应对办法-基于Weblogic服务器
    java单点登录
  • 原文地址:https://www.cnblogs.com/arbitrary/p/2645606.html
Copyright © 2011-2022 走看看