搜索小专题

本文精选了多道经典的算法题目,并提供了详细的代码实现和思路解析,涵盖DFS、BFS及优化算法等多种技术,适合不同水平的学习者参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A.Red and Black (POJ1979)

传送门

网格搜索,用dfs或者bfs写都可以。

#include <cstdio>
#include <cstring>
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
int w,h;
char s[20][21];
bool vis[20][21];
int dfs(int x,int y) {
    if (x<0||x>=h||y<0||y>=w||vis[x][y]||s[x][y]=='#')
        return 0;
    vis[x][y]=true;
    int ret=1;
    for (int i=0;i<4;++i)
        ret+=dfs(x+dx[i],y+dy[i]);
    return ret;
}
int main()
{
    while (scanf("%d%d",&w,&h)==2&&w+h) {
        for (int i=0;i<h;++i)
            scanf("%s",s[i]);
        memset(vis,0,sizeof vis);
        for (int i=0;i<h;++i)
            for (int j=0;j<w;++j)
                if (s[i][j]=='@')
                    printf("%d\n",dfs(i,j));
    }
    return 0;
}

B.Grid (Codeforces Gym 100819O)

传送门

直接bfs就可以过了,步长由一格变成了格子里的值。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define fst first
#define snd second
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int dx[4]={-1,1,0,0};
const int dy[4]={0,0,-1,1};
int n,m,d[500][500];
char s[500][500];
bool v[500][500];
queue<pii> que;
int main()
{
    memset(d,0x3f,sizeof d);
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;++i)
        scanf("%s",s[i]);
    que.push(pii(0,0));
    d[0][0]=0;
    v[0][0]=true;
    while (!que.empty()) {
        int x=que.front().fst,y=que.front().snd;
        que.pop();
        for (int i=0;i<4;++i) {
            int tx=x+dx[i]*(s[x][y]-'0'),ty=y+dy[i]*(s[x][y]-'0');
            if (tx<0||tx>=n||ty<0||ty>=m||v[tx][ty])
                continue;
            v[tx][ty]=true;
            d[tx][ty]=d[x][y]+1;
            que.push(pii(tx,ty));
        }
    }
    if (d[n-1][m-1]==inf)
        puts("IMPOSSIBLE");
    else
        printf("%d\n",d[n-1][m-1]);
    return 0;
}

C.Battleships (UVA 11953)

传送门

理解了题意之后bfs就行。

#include <bits/stdc++.h>
using namespace std;
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
int t,n,res;
char s[100][101];
bool f;
void dfs(int x,int y) {
    if (x<0||x>=n||y<0||y>=n||s[x][y]=='.')
        return;
    if (s[x][y]=='x')
        f=true;
    s[x][y]='.';
    for (int i=0;i<4;++i)
        dfs(x+dx[i],y+dy[i]);
}
int main()
{
    scanf("%d",&t);
    for (int cas=1;cas<=t;++cas) {
        scanf("%d",&n);
        for (int i=0;i<n;++i)
            scanf("%s",s[i]);
        res=0;
        for (int i=0;i<n;++i)
            for (int j=0;j<n;++j) {
                f=false;
                dfs(i,j);
                res+=f;
            }
        printf("Case %d: %d\n",cas,res);
    }
    return 0;
}

D.Jailbreak (Codeforces Gym 100625J)

传送门

题目要求最少开门数,即由最基本的步数改成了其他标准,将普通队列改成优先队列维护一下开门的数目就行。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
struct cc {
    int d,x,y;
    cc(){}
    cc(int d,int x,int y):d(d),x(x),y(y){}
    bool operator<(const cc& c2)const {
        return d>c2.d;
    }
};
int t,n,m,pt,d[3][105][105];
long long res,tt;
char s[105][105];
bool vis[105][105];
priority_queue<cc> que;
void bfs(int xx,int yy) {
    while (!que.empty())
        que.pop();
    que.push(cc(0,xx,yy));
    memset(vis,0,sizeof vis);
    vis[xx][yy]=true;
    while (!que.empty()) {
        int x=que.top().x,y=que.top().y,td=que.top().d;
        que.pop();
        d[pt][x][y]=td;
        for (int i=0;i<4;++i) {
            int tx=x+dx[i],ty=y+dy[i];
            if (tx<0||tx>n||ty<0||ty>m||vis[tx][ty]||s[tx][ty]=='*')
                continue;
            vis[tx][ty]=true;
            if (s[tx][ty]=='#')
                que.push(cc(td+1,tx,ty));
            else
                que.push(cc(td,tx,ty));
        }
    }
}
int main()
{
    scanf("%d",&t);
    while (t--) {
        pt=0;
        memset(vis,0,sizeof vis);
        scanf("%d%d",&n,&m);
        memset(s,0,sizeof s);
        memset(d,0x3f,sizeof d);
        for (int i=1;i<=n;++i)
            scanf("%s",s[i]+1);
        ++n;
        ++m;
        for (int i=0;i<=n;++i)
            s[i][m]=s[i][0]='.';
        for (int i=0;i<=m;++i)
            s[0][i]=s[n][i]='.';
        for (int i=0;i<=n;++i)
            for (int j=0;j<=m;++j)
                if (s[i][j]=='$') {
                    bfs(i,j);
                    ++pt;
                }
        bfs(0,0);
        res=inf;
        for (int i=0;i<=n;++i)
            for (int j=0;j<=m;++j) {
                tt=0;
                for (int k=0;k<3;++k)
                    tt+=d[k][i][j];
                if (s[i][j]=='#')
                    tt-=2;
                res=min(res,tt);
            }
        printf("%lld\n",res);
    }
    return 0;
}

E.Phillip and Trains (Codeforces 586D)

传送门

搜索,将火车往左移等价于主人公往右移,注意状态的设计。

#include <cstdio>
#include <cstring>
int t, n, k;
char G[3][106];
bool flag, vis[3][105][3];
void dfs(int x, int y, int t) {
    vis[x][y][t]=true;
    if (y>=100) {
        flag=true;
        return;
    }
    if (t==0&&G[x][y+1]=='.'&&!vis[x][y+1][1])
        dfs(x,y+1,1);
    if (t==1) {
        for (int i=-1; i<=1; ++i) {
            if (x+i<0||x+i>2||vis[x+i][y][2])
                continue;
            if (G[x+i][y]=='.')
                dfs(x+i,y,2);
        }
    }
    if (t==2&&G[x][y+2]=='.'&&!vis[x][y+2][0])
        dfs(x,y+2,0);

}
int main()
{
    scanf("%d",&t);
    while (t--) {
        memset(vis, 0, sizeof vis);
        scanf("%d%d%s%s%s",&n,&k,G[0],G[1],G[2]);
        for (int i=0; i<3; ++i)
            for (int j=n; j<105; ++j)
                G[i][j]='.';
        G[0][105]=G[1][105]=G[2][105]='\0';
        flag=false;
        for (int i=0; i<3; ++i)
            if (G[i][0]=='s')
                dfs(i,0,0);
        if (flag)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

F.Biridian Forest (Codeforces 329B)

传送门

从终点bfs,如果主人公到终点的距离大于等于守卫到终点的距离,守卫可以在终点等主人公,则会引发一次对战。

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define fst first
#define snd second
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
int r,c,d[1000][1000],sx,sy,res;
char s[1000][1001];
queue<pii> que;
void bfs(int xx,int yy) {
    memset(d,0x3f,sizeof d);
    que.push(pii(xx,yy));
    d[xx][yy]=0;
    while (!que.empty()) {
        int x=que.front().fst,y=que.front().snd;
        que.pop();
        for (int i=0;i<4;++i) {
            int tx=x+dx[i],ty=y+dy[i];
            if (tx<0||tx>=r||ty<0||ty>=c||s[tx][ty]=='T'||d[tx][ty]!=inf)
                continue;
            d[tx][ty]=d[x][y]+1;
            que.push(pii(tx,ty));
        }
    }
}
int main()
{
    scanf("%d%d",&r,&c);
    for (int i=0;i<r;++i)
        scanf("%s",s[i]);
    for (int i=0;i<r;++i)
        for (int j=0;j<c;++j)
            if (s[i][j]=='E')
                bfs(i,j);
            else if (s[i][j]=='S')
                sx=i,sy=j;
    for (int i=0;i<r;++i)
        for (int j=0;j<c;++j)
            if (s[i][j]>='0'&&s[i][j]<='9'&&d[i][j]<=d[sx][sy])
                res+=s[i][j]-'0';
    printf("%d\n",res);
    return 0;
}

G.Irina Tornado (Codeforces Gym 100866I)

传送门

灌水法。按照题意建图,在图的外面刘处一圈,刚开始先灌水一遍,然后把墙砸了,再灌一遍水,两次的差值就是答案了。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define fst first
#define snd second
typedef pair<int, int> pii;
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
int n, m, x1, x2, y1, y2, res;
bool G[505][505][4], vis[505][505];
void bfs(int t) {
    memset(vis, 0, sizeof vis);
    queue<pii> que;
    que.push(pii(0, 0));
    while (!que.empty()) {
        int x=que.front().fst, y=que.front().snd;
        que.pop();
        vis[x][y]=true;
        res+=t;
        for (int i=0; i<4; ++i) {
            if (G[x][y][i])
                continue;
            int tx=x+dx[i], ty=y+dy[i];
            if (tx<0||tx>=505||ty<0||ty>=505)
                continue;
            if (vis[tx][ty])
                continue;
            vis[tx][ty]=true;
            que.push(pii(tx, ty));
        }
    }
}
void get(int n, bool ok) {
    for (int i=0; i<n; ++i) {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        if (x1==x2)
        for (int i=min(y1, y2); i<max(y1, y2); ++i)
            G[x1-1][i][0]=G[x1][i][1]=ok;
        else
        for (int i=min(x1, x2); i<max(x1, x2); ++i)
            G[i][y1-1][2]=G[i][y1][3]=ok;
    }
}
int main()
{
    scanf("%d", &n);
    res=0;
    get(n, true);
    bfs(-1);
    scanf("%d", &m);
    get(m, false);
    bfs(1);
    printf("%d\n", res);
    return 0;
}

H.Comments (Codeforces 747E)

传送门

树形结构,维护一个全局指针,在树上dfs,最后按题意输出即可。

#include <cstdio>
#include <cstring>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
typedef unsigned int uii;
int n,ptr,op,cnt;
char s[1000005],t[50];
string str;
vector<string> vec[1000005];
int dfs(int p,int dep) {
    str.clear();
    while (s[p]!=',') {
        str+=s[p];
        ++p;
    }
    vec[dep].push_back(str);
    memset(t,0,sizeof t);
    op=p;
    ++p;
    while (s[p]!=','&&p<n)
        ++p;
    for (int i=op+1;i<p;++i)
        t[i-op-1]=s[i];
    int sz;
    sscanf(t,"%d",&sz);
    for (int i=0;i<sz;++i)
        p=dfs(p+1,dep+1);
    return p;
}
int main()
{
    scanf("%s",s);
    n=strlen(s);
    ptr=-1;
    while (ptr<n)
        ptr=dfs(ptr+1,0);
    for (int i=0;i<n;++i)
        if (vec[i].size()!=0)
            ++cnt;
        else
            break;
    printf("%d\n",cnt);
    for (int i=0;i<n;++i) {
        if (vec[i].size()==0)
            break;
        for (uii j=0;j<vec[i].size();++j) {
            cout<<vec[i][j];
            printf("%c",j+1==vec[i].size()?'\n':' ');
        }
    }
    return 0;
}

I.New Year and Fireworks (Codeforces 750D)

传送门

维护一下状态,可选择的一种方式是 位置、方向、第几次连锁反应 的笛卡尔积。

小技巧:后继方向的改变就是第7行注释里当前方向相邻的两个数,方便快速写出代码。

#include <bits/stdc++.h>
using namespace std;
const int dx[8]={-1,-1,-1,0,0,1,1,1};
const int dy[8]={-1,0,1,-1,1,-1,0,1};
const int d1[8]={1,0,1,0,2,3,5,4};
const int d2[8]={3,2,4,5,7,6,7,6};
/*
0 1 2
3   4
5 6 7
*/
int n,t[33],res;
bool vis[320][320][8][31],ok[320][320];
void dfs(int x,int y,int dir,int dep) {
    if (vis[x][y][dir][dep]||dep>=n)
        return;
    vis[x][y][dir][dep]=true;
    for (int i=0;i<t[dep];++i) {
        x+=dx[dir];
        y+=dy[dir];
        ok[x][y]=true;
    }
    dfs(x,y,d1[dir],dep+1);
    dfs(x,y,d2[dir],dep+1);
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;++i)
        scanf("%d",&t[i]);
    dfs(155,155,1,0);
    for (int i=0;i<310;++i)
        for (int j=0;j<310;++j)
            if (ok[i][j])
                ++res;
    printf("%d\n",res);
    return 0;
}

J.Ancient Messages (UVA 1103)

传送门

水漫法,题目说了拓扑关系不会改变,是一个重要的提示,区别古文的方式就是里面有几个圈。

输入需要处理一下,剩下的用floodfill做一做就行了。

#include <bits/stdc++.h>
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
const char al[7]={"WAKJSD"};
using namespace std;
int w,h,t,cas;
char in[205][55];
bool s[205][205],vis[205][205];
void ffill(int x,int y) {
    if (x<0||x>h||y<0||y>w||s[x][y])
        return;
    vis[x][y]=s[x][y]=true;
    for (int i=0;i<4;++i)
        ffill(x+dx[i],y+dy[i]);
}
int dfs(int x,int y) {
    if (x<0||x>h||y<0||y>w||vis[x][y])
        return 0;
    int ret=0;
    if (!s[x][y]) {
        ++ret;
        ffill(x,y);
    }
    vis[x][y]=true;
    for (int i=0;i<4;++i)
        ret+=dfs(x+dx[i],y+dy[i]);
    return ret;
}
int main()
{
    while (scanf("%d%d",&h,&w)==2&&w+h) {
        for (int i=1;i<=h;++i)
            scanf("%s",in[i]+1);
        memset(s,0,sizeof s);
        memset(vis,0,sizeof vis);
        for (int i=1;i<=h;++i)
            for (int j=1;j<=w;++j) {
                t=in[i][j]-'a'+10;
                if (t<0)
                    t+='a'-'0'-10;
                for (int k=0;k<4;++k)
                    s[i][((j-1)<<2)+4-k]=((t>>k)&1);
            }
        w<<=2;
        ++h;
        ++w;
        dfs(0,0);
        string str;
        for (int i=1;i<h;++i)
            for (int j=1;j<w;++j)
                if (!vis[i][j])
                    str+=al[dfs(i,j)];
        sort(str.begin(),str.end());
        cout<<"Case "<<++cas<<": "<<str<<endl;
    }
    return 0;
}

K.Mixed Dimensions (Codeforces 100989N)

传送门

灌水法经典题。先将图的最外一圈入优先队列,作为进水点,按水位从低到高出队,每次出队,相当于将这一点作为水源,dfs四散开去,遇到比当前位置低的就灌满,遇到比当前位置高的入队作为新的水源。

#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
struct tri {
    int x,y,h;
    tri(){}
    tri(int x,int y,int h):x(x),y(y),h(h){}
    bool operator<(const tri& t)const {
        return h>t.h;
    }
};
int r,c;
ll res,g[505][505];
bool vis[505][505];
priority_queue<tri> que;
void dfs(tri t) {
    vis[t.x][t.y]=true;
    for (int i=0;i<4;++i) {
        int tx=t.x+dx[i],ty=t.y+dy[i];
        if (tx<0||tx>=r||ty<0||ty>=c||vis[tx][ty])
            continue;
        if (g[tx][ty]<=t.h) {
            res+=(t.h-g[tx][ty]);
            dfs(tri(tx,ty,t.h));
        } else
            que.push(tri(tx,ty,g[tx][ty]));
    }
}
int main()
{
    scanf("%d%d",&r,&c);
    for (int i=0;i<r;++i)
        for (int j=0;j<c;++j) {
            scanf("%lld",&g[i][j]);
            if (!i||!j||i==r-1||j==c-1)
                que.push(tri(i,j,g[i][j]));
        }
    while (!que.empty()) {
        tri t=que.top();
        if (!vis[t.x][t.y])
            dfs(que.top());
        que.pop();
    }
    printf("%lld\n",res);
    return 0;
}

L.9 Puzzles (UVA 11513)

传送门

同8数码问题。这题多个询问,可以反向打表。

用到了一个完美哈希函数,康托展开,还原字符串的时候用到了康托逆展开。

参考链接:http://baike.baidu.com/link?url=KJkB832n07XD9Zuzf1hv9s9ziV38nOGBES1T0oGUOMPadXIdK7XnPwL_nZ05Zf1-N-5rHn0pIrmweeWSaOLeveJZ6tiGoktL7XaDTIcFCV_WzO7h9Xvwz-8MWuPFkpab

#include <bits/stdc++.h>
using namespace std;
const char a1[4]={"123"};
const char a2[3]={"HV"};
struct ss {
    string op;
    int fa;
}res[362881];
string s1,s2,in,r;
queue<int> que;
queue<char> q2;
int fac[10],h1,d,h2,t,c;
bool vis[10];
void pre() {
    fac[0]=1;
    for (int i=1;i<=9;++i)
        fac[i]=i*fac[i-1];
}
int get(string s) {
    int ret=1;
    for (int i=0;i<9;++i) {
        int c=0;
        for (int j=i+1;j<9;++j)
            if (s[j]<s[i])
                ++c;
        ret+=c*fac[8-i];
    }
    return ret;
}
string dec(int u) {
    --u;
    memset(vis,0,sizeof vis);
    string ret;
    for (int i=0;i<9;++i) {
        d=u/fac[8-i]+1;
        int j;
        for (j=0;d;++j)
            if (!vis[j])
                --d;
        --j;
        vis[j]=true;
        ret+=(j+'1');
        u%=fac[8-i];
    }
    return ret;
}
string shift(int x,int y) {
    string ret=s1;
    if (y==0) {
        swap(ret[x*3],ret[x*3+1]);
        swap(ret[x*3+1],ret[x*3+2]);
    } else {
        swap(ret[x],ret[6+x]);
        swap(ret[3+x],ret[6+x]);
    }
    return ret;
}
void bfs() {
    h1=get("123456789");
    res[h1].fa=-1;
    que.push(h1);
    while (!que.empty()) {
        h1=que.front();
        que.pop();
        s1=dec(h1);
        for (int i=0;i<3;++i)
            for (int j=0;j<2;++j) {
                s2=shift(i,j);
                h2=get(s2);
                if (res[h2].fa)
                    continue;
                res[h2].fa=h1;
                res[h2].op=a2[j];
                res[h2].op+=a1[i];
                que.push(h2);
            }
    }
}
void solve() {
    while (true) {
        in="";
        for (int i=0;i<9;++i) {
            scanf("%d",&t);
            if (!t)
                return;
            in+=t+'0';
        }
        h1=get(in);
        if (!res[h1].fa)
            puts("Not solvable");
        else {
            r="";
            while (res[h1].fa!=-1) {
                q2.push(res[h1].op[0]);
                q2.push(res[h1].op[1]);
                h1=res[h1].fa;
            }
            if (!q2.size())
                puts("0");
            else {
                printf("%d ",int(q2.size()>>1));
                while (!q2.empty()) {
                    putchar(q2.front());
                    q2.pop();
                }
                putchar('\n');
            }
        }
    }
}
int main()
{
    pre();
    bfs();
    solve();
    return 0;
}

M.Editing a Book (UVA 11212)

传送门

IDA*。因为每一层的状态数很多,且最大深度不深,仅为n-1,故可以考虑迭代加深搜索。

启发函数推导:在一个有序的排列中,一定满足ai+1-ai=1,则我们令不满足这个关系式的ai+1的个数为cnt,若当前迭代到d层,总迭代层数是tm层,则还可以进行tm-d次操作,每次操作最多能使3个数改变后继关系,所以若(tm-d)*3<cnt。则说明最好的情况下,也不可能将序列恢复成顺序排列,可以剪纸。

#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
int n,a,m,cas;
string s,g;
bool dfs(int d,string s) {
    int cnt=0;
    for (int i=1;i<n;++i)
        if (s[i]!=s[i-1]+1)
            ++cnt;
    if (!cnt)
        return true;
    if ((m-d)*3<cnt)
        return false;
    string a,b,c,e,s1;
    for (int i=0;i<n;++i)
        for (int j=i+1;j<=n;++j)
            for (int k=0;k<i;++k) {
                a=s.substr(0,k);
                b=s.substr(i,j-i);
                c=s.substr(k,i-k);
                e=s.substr(j,n-j);
                if (dfs(d+1,a+b+c+e))
                    return true;
            }
    return false;
}
int main()
{
    while (scanf("%d",&n)==1&&n) {
        s.clear();
        g.clear();
        for (int i=1;i<=n;++i) {
            scanf("%d",&a);
            s+=char(a+'0');
            g+=char(i+'0');
        }
        m=0;
        while (true) {
            if (dfs(0,s))
                break;
            ++m;
        }
        printf("Case %d: %d\n",++cas,m);
    }
    return 0;
}

N.Marble Game (UVA 1063)

传送门

Dead do.

用map记录一下已经遍历到的状态以及路径长度即可。

#include <cstdio>
#include <vector>
#include <cstring>
#include <map>
#include <queue>
using namespace std;
const int dx[4]={0,0,-1,1};
const int dy[4]={-1,1,0,0};
typedef vector<vector<int> > vvi;
int n,m,W,g[4][4],x1,y1,x2,y2,cas,res;
bool w[4][4][4][4],f;
vvi vec(4),tv(4),nv(4);
map<vvi,int> mp;
queue<vvi> que;
vvi slip(int i,int x,int y,vvi v) {
    while (true) {
        int tx=x+dx[i],ty=y+dy[i];
        if (tx<0||ty<0||tx>=n||ty>=n||w[tx][ty][x][y]||(v[tx][ty]&&v[tx][ty]<=m))
            break;
        if (v[tx][ty]-v[x][y]==m) {
            v[tx][ty]=v[x][y]=0;
            break;
        }
        if (v[tx][ty]>m) {
            f=true;
            return v;
        }
        swap(v[x][y],v[tx][ty]);
        x=tx;
        y=ty;
    }
    return v;
}
vvi turn(int id,vvi v) {
    if (!(id&1)) {
        for (int i=0;i<n;++i)
            for (int j=0;j<n;++j)
                if (v[i][j]&&v[i][j]<=m)
                    v=slip(id,i,j,v);
    } else {
        for (int i=n-1;i>=0;--i)
            for (int j=n-1;j>=0;--j)
                if (v[i][j]&&v[i][j]<=m)
                    v=slip(id,i,j,v);
    }
    return v;
}
void print(vvi v) {
    for (int i=0;i<n;++i)
        for (int j=0;j<n;++j)
            printf("%d%c",v[i][j],j+1==n?'\n':' ');
}
void bfs() {
    mp.clear();
    que.push(vec);
    mp[vec]=1;
    while (!que.empty()) {
        nv=que.front();
        que.pop();
        for (int i=0;i<4;++i) {
            f=false;
            tv=turn(i,nv);
            if (f)
                continue;
            if (mp.find(tv)!=mp.end())
                continue;
            mp[tv]=mp[nv]+1;
            que.push(tv);
        }
    }
}
int main()
{
    while (scanf("%d%d%d",&n,&m,&W)==3&&n+m+W) {
        memset(g,0,sizeof g);
        memset(w,0,sizeof w);
        for (int i=1;i<=m;++i) {
            scanf("%d%d",&x1,&y1);
            g[x1][y1]=i;
        }
        for (int i=1;i<=m;++i) {
            scanf("%d%d",&x1,&y1);
            g[x1][y1]=g[x1][y1]?0:i+m;
        }
        for (int i=0;i<4;++i)
            vec[i].clear();
        for (int i=0;i<n;++i)
            for (int j=0;j<n;++j)
                vec[i].push_back(g[i][j]);
        for (int i=0;i<W;++i) {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            w[x1][y1][x2][y2]=true;
            w[x2][y2][x1][y1]=true;
        }
        bfs();
        for (int i=0;i<n;++i) {
            vec[i].clear();
            for (int j=0;j<n;++j)
                vec[i].push_back(0);
        }
        printf("Case %d: ",++cas);
        if (mp.find(vec)==mp.end())
            puts("impossible\n");
        else
            printf("%d moves\n\n",mp[vec]-1);
    }
    return 0;
}


O.Gears (Codeforces Gym 100819V)

传送门

题意看起来很吓人,仔细看一下题目中的齿轮密度无限大的提示,发现其实没有那么复杂。

性质:

1.齿轮连接处线速度是一样的,角速度则是半径之比的倒数。

2.所以如果从起点出发,有边为奇数的圈的话,第0号齿轮不能转动。

3.0号齿轮和n-1号齿轮不连通,则不能带动n-1号齿轮转动。

解法:

建图:相切的圆之间连边,代表齿轮接触。

以0号齿轮为起点染色判断是否是二分图。不是的话证明有奇圈,非法。

是二分图则判断终点是否被染色:

若没有,则不能带动终点齿轮转动;

若有且颜色相同,则同向转;若颜色相反,则反向转。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef unsigned int uii;
int n,x[1000],y[1000],r[1000],gcd,col[1000];
vector<int> vec[1000];
inline int sqr(int x) {
    return x*x;
}
int check(int i,int j) {
    return sqr(r[i]+r[j])==sqr(x[i]-x[j])+sqr(y[i]-y[j]);
}
bool dfs(int u,int co) {
    col[u]=co;
    for (uii i=0;i<vec[u].size();++i) {
        int v=vec[u][i];
        if (col[v]) {
            if (col[u]==col[v])
                return false;
            continue;
        }
        if (!dfs(v,-1*co))
            return false;
    }
    return true;
}
int main()
{
    scanf("%d",&n);
    memset(col,0,sizeof col);
    for (int i=0;i<n;++i) {
        scanf("%d%d%d",&x[i],&y[i],&r[i]);
        vec[i].clear();
    }
    for (int i=0;i<n;++i)
        for (int j=i+1;j<n;++j)
            if (check(i,j)) {
                vec[i].push_back(j);
                vec[j].push_back(i);
            }
    if (!dfs(0,1))
        puts("The input gear cannot move.");
    else if (!col[n-1])
        puts("The input gear is not connected to the output gear.");
    else {
        gcd=__gcd(r[0],r[n-1]);
        if (col[0]!=col[n-1])
            putchar('-');
        printf("%d:%d\n",r[0]/gcd,r[n-1]/gcd);
    }
    return 0;
}




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值