zoukankan      html  css  js  c++  java
  • 算法竞赛入门经典 训练指南 之 图论(完全版持续更新)

    由于题目太多了,所以在更新过程中可能会出错,欢迎大家一起交流^_^。。。

    第一题 题目:uva 11624 Fire 
    二维迷宫中,有一些着火点(可能不止一个),给出你的初始位置,人与着火点都是每秒钟向相邻的格子移动一格,问人能不能够在火势到达之前走到边界,如果能够,输出最短的时间

    分析:对于着火点,可以预处理把所有的着火点都放进队列中,然后再用BFS处理出所有可燃烧的格子的最短到达时间。再用BFS来计算出人能够达到边界的最短间。。。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    
    using namespace std;
    
    const int X = 1005;
    
    int dirx[] = {-1,1,0,0};
    int diry[] = {0,0,-1,1};
    
    int n;
    int m;
    bool use[X][X];
    int tt[X][X];
    char map[X][X];
    
    struct node{
    	int x,y,step;
        node(int _x,int _y,int _step){
    		x = _x;
    		y = _y;
    		step = _step;
    	}
    };
    
    struct point{
    	int x,y,step;
    	point(){}
    	point(int _x,int _y,int _step){
    		x = _x;
    		y = _y;
    		step = _step;
    	}
    }q[X*X];
    
    bool out(int x,int y){
        return x>=n||y>=m||x<0||y<0;
    }
    
    int bfs(int sx,int sy){
        memset(use,false,sizeof(use));
        int head = 0;
        int tail = 0;
        q[tail++] = point(sx,sy,0);
    
        while(head<tail){
            point pre = q[head++];
            int x = pre.x;
            int y = pre.y;
    
            if(x==0||y==0||x==n-1||y==m-1)
                return pre.step;
    
            pre.step ++;
            for(int i=0;i<4;i++){
                int dx = dirx[i]+x;
                int dy = diry[i]+y;
                if(out(dx,dy))
                    continue;
                if(use[dx][dy]||tt[dx][dy]<=pre.step||map[dx][dy]=='#')
                    continue;
                use[dx][dy] = true;
                q[tail++] = point(dx,dy,pre.step);
            }
        }
        return -1;
    }
    
    void init(){
        memset(use,false,sizeof(use));
        int head = 0;
        int tail = 0;
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(map[i][j]=='F')
                    q[tail++] = point(i,j,0);
        while(head<tail){
            point pre = q[head++];
            int x = pre.x;
            int y = pre.y;
            tt[x][y] = min(tt[x][y],pre.step ++);
    
            for(int i=0;i<4;i++){
                int dx = dirx[i]+x;
                int dy = diry[i]+y;
                if(out(dx,dy))
                    continue;
                if(use[dx][dy]||map[dx][dy]=='#')
                    continue;
                use[dx][dy] = true;
                q[tail++] = point(dx,dy,pre.step);
            }
        }
    }
    
    void solve(){
        cin>>n;
        cin>>m;
        for(int i=0;i<n;i++)
            scanf("%s",map[i]);
        memset(tt,0x3f,sizeof(tt));
        int sx,sy;
        sx = sy = 0;
    
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
                if(map[i][j]=='J'){
                    sx = i;
                    sy = j;
                }
    
        init();
    
        int ok = bfs(sx,sy);
        if(ok==-1)
            puts("IMPOSSIBLE");
        else
            printf("%d\n",ok+1);
    }
    
    int main(){
        freopen("sum.in","r",stdin);
        int ncase;
        cin>>ncase;
        while( ncase-- != 0 )
            solve();
        return 0;
    }
    

      

    第二题 题目:uva 10047 The Monocycle  
    一辆独轮车等分分成了5个块,每个块分别有一种颜色,刚开始时独轮车的朝向为北,独轮车的底部为绿色,现在独轮车每秒可以先前移动一个格子,或者把方向移到向左或向右。现在问如何移动独轮车,使得独轮车到达终点且底部颜色为绿色的最短时间。(二维迷宫,若遇到障碍物不能向前移动)

    分析:在原来的二维迷宫问题上增加二维,visit[x][y][direction][color],表示格子[x,y]中朝向为direction并以颜色color走过了的就不再重复走。其他的跟二维迷宫基本一样~~

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    
    using namespace std;
    
    const int X = 26;
    const int direction = 4;
    const int color = 5;
    
    bool use[X][X][direction][color];
    int n,m;
    char map[X][X];
    int sx,sy,ex,ey;
    
    int dirx[] = {-1,0,1,0};
    int diry[] = {0,1,0,-1};
    
    struct node{
        int x,y;
        int d,c;
        int step;
        node(){}
        node(int _x,int _y,int _d,int _c,int _step):x(_x),y(_y),d(_d),c(_c),step(_step){}
    };
    
    bool out(int x,int y){
        return x<0||y<0||x>=n||y>=m;
    }
    
    int Abs(int x){
        return max(x,-x);
    }
    
    void bfs(){
        memset(use,false,sizeof(use));
        queue<node> q;
        use[sx][sy][0][0] = true;
        q.push(node(sx,sy,0,0,0));
        while(q.size()){
            node pre = q.front();
            q.pop();
            int x = pre.x;
            int y = pre.y;
            int d = pre.d;
            int c = pre.c;
            if(x==ex&&y==ey&&pre.c==0){
                printf("minimum time = %d sec\n",pre.step);
                return;
            }
            pre.step ++;
            for(int i=0;i<4;i++){
                if(Abs(i-d)==2)
                    continue;
                if(i==d){
                    int dx = dirx[i]+x;
                    int dy = diry[i]+y;
                    int dc = (c+1)%5;
                    int dd = i;
                    if(out(dx,dy)||map[dx][dy]=='#'||use[dx][dy][dd][dc])
                        continue;
                    use[dx][dy][dd][dc] = true;
                    q.push(node(dx,dy,dd,dc,pre.step));
                }
                else{
                    if(use[x][y][i][c])
                        continue;
                    use[x][y][i][c] = true;
                    q.push(node(x,y,i,c,pre.step));
                }
            }
        }
    
        puts("destination not reachable");
    }
    
    int main(){
        freopen("sum.in","r",stdin);
        int ncase = 0;
        while(cin>>n>>m,n||m){
            if(ncase)
                puts("");
            printf("Case #%d\n",++ncase);
            for(int i=0;i<n;i++){
                scanf("%s",map[i]);
                for(int j=0;j<m;j++){
                    if(map[i][j]=='S'){
                        sx = i;
                        sy = j;
                    }
                    else if(map[i][j]=='T'){
                        ex = i;
                        ey = j;
                    }
                }
            }
            bfs();
        }
        return 0;
    }
    

      

    第三题 题目: uva 10054 The Necklace
    珠子由两部分组成,每个部分都有颜色,若相同颜色的两个珠子可以串连在一起,现在问能不能把所有的珠子串连在一起。若能够,给出串联的方式

    分析:求欧拉回路,把每种颜色看做图上的一个顶点,然后对于每个珠子构造一条无向边,先判断是否有奇数度的顶点,若有的话,欧拉回路不存在,否则DFS一次即可。另外注意图中可能存在多条相同的边(我没测过),所以每次DFS的时候把边删掉一条即可

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int X = 60;
    const int n = 50;
    
    int map[X][X];
    int id[X];
    int m;
    
    void dfs(int x){
        for(int y=1;y<=n;y++){
            if(map[x][y]>0){
                map[x][y] --;
                map[y][x] --;
                dfs(y);
                cout<<y<<" "<<x<<endl;
            }
        }
    }
    
    int main(){
        freopen("sum.in","r",stdin);
        int ncase,x,y,cnt = 0;
        cin>>ncase;
        while(ncase--){
            if(cnt)
                puts("");
            printf("Case #%d\n",++cnt);
            memset(map,0,sizeof(map));
            memset(id,0,sizeof(id));
            scanf("%d",&m);
            while(m--){
                scanf("%d%d",&x,&y);
                map[x][y] ++;
                map[y][x] ++;
    
                id[x] ++;
                id[y] ++;
            }
            bool ok = true;
            for(int i=1;i<=n;i++){
                if(id[i]&1){
                    ok = false;
                    puts("some beads may be lost");
                    break;
                }
            }
            if(!ok)
                continue;
            dfs(x);
        }
        return 0;
    }

    第七题 题目:icpc 4287 Proving Equivalences
    a能够推出b,b能够推出c,现在问增加多少个关系,使得所有的关系相互等价

    分析:不难想到构造出有向图,然后求出图中的强连通分量,缩点之后求max(id,od)。id表示所有入度为0的顶点个数,od表示出度为0的个数,但是得要注意的是当连通块只有一个的时候,答案应该是0(缩点里的所有元素相互等价)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int maxn =  50005;
    const int maxm =  100005;
    
    int po[maxn],tol;
    int stack[maxn],dfn[maxn],low[maxn],father[maxn],bcnt,depth,top;
    bool instack[maxn];
    int id[maxn],od[maxn];
    int n,m;
    
    struct node{
        int y,next;
    }edge[maxm];
    
    void dfs(int x){	//递归实现tarjan算法
        low[x] = dfn[x] = ++depth;
        instack[x] = true;
        stack[++top] = x;
        int y;
        for(int i=po[x];i;i=edge[i].next){
            y = edge[i].y;
            if(!dfn[y]){
                dfs(y);
                low[x] = min(low[x],low[y]);
            }
            else if(instack[y])
                low[x] = min(low[x],dfn[y]);
        }
        if(low[x]==dfn[x]){
            ++bcnt;
            do{
                y = stack[top--];
                instack[y] = false;
                father[y] = bcnt;
            }while(x!=y);
        }
    }
    
    void tarjan(){
        memset(instack,false,sizeof(instack));
        memset(dfn,0,sizeof(dfn));
        top = bcnt = depth = 0;
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                dfs(i);
        if(bcnt==1){
            puts("0");
            return;
        }
    
        memset(id,0,sizeof(id));
        memset(od,0,sizeof(od));
        for(int x=1;x<=n;x++)
            for(int j=po[x];j;j=edge[j].next){
                int y = edge[j].y;
                if(father[x]!=father[y]){
                    od[father[x]] ++;
                    id[father[y]] ++;
                }
            }
        int id_cnt = 0,od_cnt = 0;
        for(int i=1;i<=bcnt;i++){
            if(!id[i])
                id_cnt ++;
            if(!od[i])
                od_cnt ++;
        }
        cout<<max(id_cnt,od_cnt)<<endl;
    }
    
    void add(int x,int y){
        edge[++tol].y = y;
        edge[tol].next = po[x];
        po[x] = tol;
    }
    
    int main(){
        freopen("sum.in","r",stdin);
        int ncase;
        cin>>ncase;
        while(ncase--){
            memset(po,0,sizeof(po));
            tol = 0;
            int x,y;
            cin>>n>>m;
            while(m--){
                scanf("%d%d",&x,&y);
                add(x,y);
            }
            tarjan();
        }
        return 0;
    }
    

      

    第八题 题目:uva 11324 The Largest Clique
    从图中某点出发,求最远能够一次走过多少个节点

    分析:
    tarjan求gcc,然后构造出新图,新图是一个dag,对于dag上用dp求出最长路径即可。dp转移方程为dp[x] = size[x] + max(dp[y]); 缩点后有边x到y的边,记忆化搜索就行了,具体看实现代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    
    using namespace std;
    
    const int maxn = 1005;
    const int maxm = 50005;
    
    #define debug puts("here");
    
    int dfn[maxn],low[maxn],stack[maxn],father[maxn],bcnt,top,depth;
    bool instack[maxn];
    int po[maxn],tol,n,m;
    int id[maxn];
    int dp[maxn];
    int sum[maxn];
    
    vector<int> vec[maxn];
    
    struct node{
        int y,next;
    }edge[maxm];
    
    void add(int x,int y){
        edge[++tol].y = y;
        edge[tol].next = po[x];
        po[x] = tol;
    }
    
    void dfs(int x){	//递归实现tarjan算法
        low[x] = dfn[x] = ++depth;
        instack[x] = true;
        stack[++top] = x;
        int y;
        for(int i=po[x];i;i=edge[i].next){
            y = edge[i].y;
            if(!dfn[y]){
                dfs(y);
                low[x] = min(low[x],low[y]);
            }
            else if(instack[y])
                low[x] = min(low[x],dfn[y]);
        }
        if(low[x]==dfn[x]){
            ++bcnt;
            do{
                y = stack[top--];
                instack[y] = false;
                father[y] = bcnt;
            }while(x!=y);
        }
    }
    
    void tarjan(){
    memset(instack,false,sizeof(instack)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); top = bcnt = depth = 0; for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); } int f(int x){ //记忆化方法求dag上的最长路径 if(dp[x]) return dp[x]; int ans = 0; for(int i=0;i<(int)vec[x].size();i++){ //从x的所有边出发,求出最大的路径 int y = vec[x][i]; ans = max(ans,f(y)); //转移方程 } dp[x] = ans+sum[x]; return dp[x]; } void dag(){ memset(id,0,sizeof(id)); memset(sum,0,sizeof(sum)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) vec[i].clear(); for(int x=1;x<=n;x++){ //构造新图 for(int j=po[x];j;j=edge[j].next){ int y = edge[j].y; if(father[x]!=father[y]){ vec[father[x]].push_back(father[y]); id[father[y]] ++; } } sum[father[x]] ++; //统计每个缩点后的该节点所包含的所有原图的节点数目 } int ans = 0; for(int i=1;i<=bcnt;i++) if(!id[i]) ans = max(f(i),ans); cout<<ans<<endl; } int main(){ freopen("sum.in","r",stdin); int ncase; cin>>ncase; while(ncase--){ cin>>n>>m; int x,y; memset(po,0,sizeof(po)); tol = 0; while(m--){ scanf("%d%d",&x,&y); add(x,y); } tarjan(); dag(); } return 0; }

      

    。。。。。

  • 相关阅读:
    COS和CDN的关系
    【转】WebGL 着色器和GLSL
    【转】前端最新性能指标
    【转】理解音视频 PTS 和 DTS
    HLS
    【转】带有function的JSON对象的序列化与还原
    环信Demo 导入错误
    安卓中 使用html来使文字变色Html.fromHtml
    第三方下载控件 用起来还是不错的偶!Aria
    网络文件下载
  • 原文地址:https://www.cnblogs.com/yejinru/p/2752606.html
Copyright © 2011-2022 走看看