codeforces contest 189

http://codeforces.com/contest/189

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

D 187B AlgoRace

题意:n个城市,m辆车,r场比赛。给出m辆车在n个城市中的最短路。r场比赛给出s(起点),t(终点),k(允许换乘次数)

题解:首先预处理出m辆车在n个城市中的最短路(其实我只会用dij)。d[i][j][k]表示i到j换乘了k次。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
int a[65][65][65],b[65][65][65],d[65][65][1005],vis[65];//s,t,k
int n,m,r;
void Dijkstra(int x,int y) {
	memset(vis,0,sizeof(vis));
	vis[y]=1;
	for(int i=1;i<=n;++i) b[x][y][i]=a[x][y][i];
	for(int i=1;i<n;++i) {
		int ans=0x3f3f3f,pos;
		for(int j=1;j<=n;++j) {
			if(!vis[j]&&b[x][y][j]<ans) {
				ans=b[x][y][j];
				pos=j;
			}
		}
		vis[pos]=1;
		for(int j=1;j<=n;++j) {
			if(!vis[j]&&b[x][y][pos]+a[x][pos][j]<b[x][y][j]) {
				b[x][y][j]=b[x][y][pos]+a[x][pos][j];
			}
		}
	}
}
int main() {
    while(~scanf("%d%d%d",&n,&m,&r)) {
    	//第i辆车,j到k 
        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                for(int k=1;k<=n;++k) scanf("%d",&a[i][j][k]);
            }
        }
        memset(b,0x3f3f3f,sizeof(b));
        memset(d,0x3f3f3f,sizeof(d));
        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                Dijkstra(i,j);//第i辆车,以j为起点
                for(int k=1;k<=n;++k) {
                    d[j][k][0]=min(d[j][k][0],b[i][j][k]);
                }
            }
        }
        //i到j换k次
        for(int k=1;k<=65;++k) {
            for(int i=1;i<=n;++i) {
                for(int j=1;j<=n;++j) {
                    for(int x=1;x<=n;++x) {
                        d[i][j][k]=min(d[i][j][k],d[i][x][k-1]+d[x][j][0]);
                    }
                }
            }
        }

        int x,y,z;
        for(int i=1;i<=r;++i) {
            scanf("%d%d%d",&x,&y,&z);
            if(z>65) z=65;
            printf("%d\n",d[x][y][z]);
        }
    }
    return 0;
}



//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

题意:n个点,m条边,k个特殊点。起点s,终点t。有一个健忘的人要从s走到t,但是他从特殊点开始走ans个点就不走了。请问ans最小是多少。(保证起点一定是特殊点)

题解:二分答案。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=100005;
int vol[N],last[N],sz,val[N],vis[N],in[N];
struct Edge {
    int pre,to;
}e[4*N];
void addEdge(int u,int v) {
    e[++sz].to=v;
    e[sz].pre=last[u];
    last[u]=sz;
}
void initE() {
    sz=0;
    memset(last,-1,sizeof(last));
}
int n,m,k,s,t;
bool check(int dis) {
    memset(vis,0,sizeof(vis));//有没有被访问过 
    memset(val,0,sizeof(val)); 
    memset(in,0,sizeof(in));//有没有在队列里 
    queue<int> que;
    vis[s]=1;val[s]=dis;in[s]=1;
    que.push(s);
    while(!que.empty()) {
        int u=que.front();que.pop();in[u]=0;
        if(t==u) return 1;
        for(int i=last[u];i!=-1;i=e[i].pre) {
            int v=e[i].to;
            if(t==v) return 1;
            vis[v]=1;
            int tt;
            if(vol[v]) tt=dis;
            else tt=val[u]-1;
            if(tt>val[v]) {
                val[v]=tt;
                if(!in[v]) {
                    que.push(v);
                    in[v]=1;
                }
            }
        }
    }
    return vis[t];
}
int main() {
    while(~scanf("%d%d%d",&n,&m,&k)) {
        memset(vol,0,sizeof(vol));
        for(int i=1;i<=k;++i) {
            int x;scanf("%d",&x);
            vol[x]=1;
        }
        initE();int u,v;
        for(int i=1;i<=m;++i) {
            scanf("%d%d",&u,&v);
            addEdge(u,v);
            addEdge(v,u);
        }
        scanf("%d%d",&s,&t);
        int l=1,r=n,ans=-1;
        while(l<=r) {
            int mid=l+r>>1;
            if(check(mid)) {
                ans=mid;
                r=mid-1;
            } else {
                l=mid+1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值