dfs練習(一)

題目:Badge

思路:簡單的dfs,因爲只用一個分支,當再次回來的時候就是結果。
代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;

int a[maxn], tot = 0;
int n, vis[maxn], ans = 0;
int dfs(int u)
{
    vis[u] = 1;
    int t = a[u];
    if(!vis[t])
        dfs(t);
    else return t;//如果回到之前標記的就是結果
}
int main()
{
    int v;
    scanf("%d", &n);
    tot = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof(vis));
        ans = 0;
        if(i != 1)printf(" ");
        printf("%d", dfs(i));
    }
    printf("\n");
    return 0;
}

題目:Protect Sheep

題意:就是給你一個圖,圖上有羊和狼,不讓狼吃羊,你必須在用狗去把狼隔開,注意不用考慮放置狗的個數最少You don’t have to minimize the number of dogs
思路:既然不用考慮夠的個數最少,那麼只需要,判斷羊的四周是否是狼,如果是狼那麼就輸出NO,否則輸出YES以及放置後的地圖。
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 505;
char mp[maxn][maxn];
int vis[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0}, n, m;
int flag = 1;

int main(){
    scanf("%d %d", &n, &m);
    for(int i = 0; i < n; i++){
        scanf("%s", mp[i]);
    }
    flag = 1;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            if(mp[i][j] == 'S'){
                if(mp[i][j-1] == 'W'|| mp[i-1][j] == 'W' ||mp[i+1][j] == 'W'|| mp[i][j+1] == 'W'){
                    printf("NO\n");
                    return 0;
                }
            }
        }
    }
    printf("YES\n");
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            if(mp[i][j] == '.')printf("D");
            else printf("%c", mp[i][j]);
        }
        printf("\n");
    }

return 0;
}


題目:King Escape

題意:本題的大體思路就是給你三個點(ax, ay),(bx, by),(cx, cy),問你從(bx,by)這個點能否不經過與(ax, ay)同行同列同一對角線到達(cx, cy),如果能輸出YES,否則輸出NO
思路:本題的主體思路就是類似與dfs走迷宮的那類題,但是本題需要加入一些判斷,使走過的路線不與(ax, ay)同行同列同一對角線,不同行不同類好比較。但是不同對角線如何比較,通過觀察圖發現,同一對角線斜路是-1或者1.然後在通過y = x+b或者y = -x+b,把(ax, ay)這個點帶入,就能求出b,然後根據方程判斷是否與(ax, ay)在同一對角線上。
本題還可以以(ax,by)作爲原點建立直角座標系,如果(bx,by)和(cx,cy)在同一象限那麼就輸出YES,,否則輸出NO。爲什麼可以這麼做你可以看圖找一下規律。
參考博客:CF1033A. King Escape的題解
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1010;
int ax, ay, bx, by, cx, cy, n, b1, b2;
bool Judge1(int x, int y){
     if(y == x+b1)return true;
     else return false;
}
bool Judge2(int x, int y){
     if(y == -x+b2)return true;
     else return false;
}
int dx[10] = {0,0,1,1,1,-1,-1,-1};//八個方向
int dy[10] = {1,-1,1,-1,0,1,-1,0};
int vis[maxn][maxn];//標記
int flag =  0;
bool dfs(int x, int y){
    vis[x][y] = 1;
    if(x == cx && y == cy){//到達終點
        flag = 1;
        return true;
    }
    for(int i = 0; i < 8; i++){
        int tx, ty;
        tx = x+dx[i];
        ty = y+dy[i];
        if(tx >= 1 && tx <= n && ty >= 1 && ty <= n && !vis[tx][ty] && !Judge1(x, y) && !Judge2(x, y) && tx != bx && ty != by){//判斷
            vis[tx][ty] = 1;
            if(dfs(tx, ty))return true;//遞歸
        }
    }
    return false;
}
int main(){

    memset(vis, 0, sizeof vis);
    scanf("%d", &n);
    scanf("%d %d", &bx, &by);
    scanf("%d %d", &ax, &ay);
    scanf("%d %d", &cx, &cy);
    b1 = by-bx;
    b2 = bx+by;
    int t = dfs(ax, ay);
    if(t)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;


return 0;
}

題目:New Year Transportation

題意:本題的題意就是給你i點可以到大(i+ai),問你是否能到大K,如果能到達輸出YES,否則話輸出NO。因爲本題已經約束了1<ai<n-i,所以不用考慮i+ai越界。
思路:本題就是一道水題,簡單的dfs就能解決。
代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e4+10;
int a[maxn], n, k;
bool dfs(int x){

     if(x > k)return false;//因爲不能回退,所以當x>k時就已經沒有搜到
     if(x == k)return true;
     dfs(x+a[x]);
     //return false;
}
int main(){
    scanf("%d %d", &n, &k);
    for(int i = 1; i < n; i++)scanf("%d", &a[i]);
    int t = dfs(1);
    if(t)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
return 0;
}


題目:Transformation: from A to B

題意:給你兩個數a, b,讓你通過一兩種操作把a變成b,這兩中操作分別是x–>2x, x–>x10+1;通過這兩種操作如果不能變成b輸出NO,如果能就輸出YES 操作數 變換的過程
思路:一個思路就是簡單的dfs,另外一個就是根據性質去推從b往前推a,如果b是偶數那麼b就除以2,如果b爲奇數那麼就是通過10*x+1這個步驟。
參考博客:CodeForces–TechnoCup–2016.10.15–ProblemA–Transformation: from A to B
dfs代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
#define LL long long
const int INF = 100000;
int vis[maxn], val[maxn],ans = INF;//val存儲路徑
LL a, b;
void dfs(LL x, LL step){
     if(x == b){//如果到達終點
        if(step < ans){//步數小於當前的步數
            for(int i = 0; i < step; i++){//更新
                val[i] = vis[i];
            }
            ans = step;
        }
        return;
     }
     if(x*2 <= b){//乘2的操作
        vis[step] = x*2;
        dfs(x*2, step+1);
        vis[step] = 0;
     }
     if(x * 10 + 1 <= b){//後面補1的操作
        vis[step] = x*10+1;
        dfs(x*10+1, step+1);
        vis[step] = 0;
     }
}
int main(){
    scanf("%lld %lld", &a, &b);
    dfs(a, 0);
    if(ans == INF)printf("NO\n");
    else {
        printf("YES\n");
        printf("%d\n", ans+1);
        printf("%d", a);
        for(int i = 0; i < ans; i++){
            printf(" %d", val[i]);
        }
        printf("\n");
    }
return 0;
}

題目:The Way to Home

題意:本題的題意就是給你一個長度爲n的字符串,讓你從1開始走,走到n,每次走的距離不能超過d,而且每次走的時候只能走數值爲1的點,如果可以到達n,輸出結果最小步數,否則輸出-1
思路:本題有兩種思路一個是dfs,一個數模擬,先說dfs吧,首先我設當前結點爲x,然後從k最小開始dfs,然後在到達n點的時候去更新最小值,後來發現跑到第15組就超時了,我感覺這種方法存在問題,從k最大開始搜索這麼就不用去更新最小值了,因爲這麼做一定是最優的,結果跑到44組就超時了,雖然有一定的優化但是還是存在一定問題,我就想可不可以用數組去標記,仔細思考了一下,發現是可以的,爲什麼是可以的如果我把當前結點標記了如果搜索失敗在回溯的時候,如果已經標記了那麼證明從該點的路徑是不可達的,所以不能走。
模擬的思想其實也差不多,每一次儘量走最大的如果不能走就往前挪一下,重複上述操作。參考博客:The Way to Home (模擬)
dfs代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
char str[maxn];
int n, k, ans = maxn;
bool vis[maxn];
bool dfs(int x, int step){
     if(x == n){//到達n點
        ans = step;
        return true;
     }
     for(int i = k; i > 0; i--){
        if(str[x+i] == '1' && !vis[x+i]){
            vis[x+i] = 1;
            if(dfs(x+i, step+1))return true;

        }
     }
     return false;
}
int main(){
    scanf("%d %d", &n, &k);
    scanf("%s", str+1);
    ans = maxn;
    dfs(1,0);
    if(ans == maxn)printf("-1\n");
    else printf("%d\n", ans);

return 0;
}

題目:DZY Loves Chessboard

題目:本題題意就是給你一個矩陣,"."代表可以放置棋子的,“-”代表不可以放置棋子,然後讓你把所有相鄰的可以放置棋子的位置放置不同的顏色的棋子,如果只能放置一個棋子的這種情況所以放置。
思路:就是一個迷宮類型的搜索的簡單變形,只需要增加一個變量就可以,用它來記錄之前的情況。
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 105;
char a[maxn][maxn];
bool vis[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1,0, 0};
int n, m;
void dfs(int x, int y, int flag)
{
    if(!flag)a[x][y] = 'B';
    else a[x][y] = 'W';
    for(int i = 0; i < 4; i++)
    {
        int tx, ty;
        tx = x + dx[i];
        ty = y + dy[i];
        if(tx >= 0 && tx < n && ty >=0 && ty < m && a[tx][ty] == '.')
        {
            dfs(tx,ty,!flag);
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 0; i < n; i++)
    {
        scanf("%s", a[i]);
    }
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            if(a[i][j] == '.')dfs(i, j, 0);
        }
    }
    for(int i = 0; i < n; i++)printf("%s\n", a[i]);
    return 0;
}

題目:Students and Shoelaces

題意:本題的題意就是你通過取消只與一個點相連所有點,然後在重複操作,統計你的操作個數。
思路:本題就是一個拓撲排序,因爲是無向圖,所以所有點都是入度,然後找到入度爲1的點。
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 105;
int a[maxn][maxn];
bool vis[maxn][maxn];
int n, m;
int degree[maxn];

queue<int>q;
int cn = 1, ans = 0, tmp;
void TopSort(int n)
{
    int cnt = 0;
    for(int i = 1; i <= n; i++)
    {
        if(degree[i] == 1)
        {
            q.push(i);
            cnt++;
        }

    }
    if(cnt)ans++;//如果存在入度爲1的點,操作次數加一
    while(!q.empty())
    {
        tmp = q.front();
        q.pop();
        for(int i = 1; i <= n; i++)
        {
            if(a[tmp][i])
            {
                degree[i]--;
                degree[tmp]--;

            }
        }
        cnt--;
        if(cnt == 0)//如果第一次的都已經完事了
        {

            for(int i = 1; i <= n; i++)//開始下一步
            {
                if(degree[i] == 1)
                {
                    q.push(i);
                    cnt++;
                }

            }
            if(cnt)ans++;//如果下一步存在,結果加一
        }
    }
}
int main()
{

    int u, v;
    ans = 0;
    scanf("%d %d", &n, &m);
    for(int i = 0; i < m; i++)
    {
        scanf("%d %d", &u, &v);
        a[u][v] = a[v][u] = 1;
        degree[u]++;
        degree[v]++;
    }
    TopSort(n);
    cout<<ans<<endl;
    return 0;
}

題目:Party

題意:本題的題意就是把不同的不存在上級的人放在同一組中。
思路:本題的思想非常簡單,就是幾個建圖加上拓撲排序就完事了,如果存在沒有上級的那種就不用處理了。
代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2005;
int degree[maxn];
int mp[maxn][maxn];
struct Node{
       int id, x;
}tmp;
int ans = 0, n;
queue<Node>q;
void TopSort(){//拓撲排序
     for(int i = 1; i <= n; i++){
        if(!degree[i]){
           tmp.id = 1;
           tmp.x = i;
           q.push(tmp);
        }

     }
     while(!q.empty()){
        tmp = q.front();
        int x = tmp.x;
        int id = tmp.id;
        q.pop();
        ans = max(ans, id);
        for(int i = 1; i <= n; i++){
            if(mp[x][i]){
                degree[i]--;
                if(!degree[i]){
                    tmp.x = i;
                    tmp.id = id+1;
                    q.push(tmp);
                }
            }
        }
     }
}
int main(){
    int  t;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d",&t);
        if(t == -1)continue;//不存在上級的這種情況
        mp[t][i] = 1;//反向建圖
        degree[i]++;
    }
    TopSort();
    printf("%d\n", ans);

return 0;
}

題目:RumorRumor

題意:就是告訴你賄賂每一個人要多少錢,朋友之間可以免費互傳消息,所以問你最小的花費。
思路:本題可以利用並查集寫,也可以用dfs寫。我只列出dfs代碼。
並查集代碼:參考博客:codeforces 893 C.Rumor
dfs代碼:

#include<bits/stdc++.h>
using namespace std;
const long long INF = 1e9+1;
const int maxn = 1e5+10;
int vis[maxn], val[maxn];
int ne[maxn*2], e[maxn*2],h[maxn*2], tot = 0;
void add(int u, int v)//鏈式前向星存儲,注意無向圖,所以需要開2*maxn
{
    e[tot] = v;
    ne[tot] = h[u];
    h[u] = tot++;
}
int ans;
void dfs(int u)//搜索
{
    vis[u] = 1;
    ans = min(ans, val[u]);
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int t = e[i];
        if(!vis[t])
        {

            dfs(t);
        }
    }
}
int main()
{
    int n, m, u, v;
    scanf("%d %d", &n, &m);
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= n; i++)scanf("%d", &val[i]);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d %d", &u, &v);
        add(u, v);
        add(v,u);
    }
    long long sum = 0;

    for(int i = 1; i <= n; i++)
    {
        ans = INF;
        if(!vis[i])//如果沒有搜索過
        {
            dfs(i);
            sum += ans;
        }
    }
    printf("%lld\n", sum);
    return 0;
}

題目:Plus from Picture

題意:本題的題意就是給你一個圖,當這張圖上當且僅當只有一個由* 圍成的+號,並且除了這個加好的四個方向上有*, 其他地方不能有 *這種情況下輸出YES,否則輸出NO
思路:本題就是一個暴力,首先找到中心點如果找到僅找到一箇中心點,那麼就以這個點爲中心向四周擴展,標記擴展經過的點,然後暴力一遍,有沒有沒有經過的點,那麼就是NO,沒有的話就是YES,找到多個或者沒有找到中心點也是NO
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 505;
char mp[maxn][maxn];
int vis[maxn][maxn];
int n, m;
int main()
{
    scanf("%d %d", &n, &m);
    for(int i =  0; i < n; i++)
    {
        scanf("%s", &mp[i]);
    }
    int st, en, flag = 0;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            if(mp[i][j] == '*' && i > 0 && i < n-1 && j > 0 && j < m-1)//找中心點
            {
                if(mp[i][j-1] == '*' && mp[i-1][j] == '*' && mp[i][j+1] == '*' && mp[i+1][j] == '*')
                {
                    flag++;
                    st = i;
                    en = j;
                }
            }
        }
    }
    if(flag == 1)
    {
        vis[st][en] = 1;//別忘了把中心點標記一下
        for(int i = st+1; mp[i][en] == '*'; i++)vis[i][en] = 1;
        for(int i = st-1; mp[i][en] == '*'; i--)vis[i][en] = 1;
        for(int i = en+1; mp[st][i] == '*'; i++)vis[st][i] = 1;
        for(int i = en-1; mp[st][i] == '*'; i--)vis[st][i] = 1;
        int k = 0;
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < m; j++)
            {
                if(mp[i][j] == '*' && !vis[i][j]){
                    k = 1;
                    break;
                }
            }
        }
        if(k)cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
    else cout<<"NO"<<endl;
    return 0;
}

題目:Coloring a Tree

題意:本題的題意就是給你一顆樹,每一個節點都應該染成他所需要的顏色,但是在染色的同時會同時把他的子樹給染色了,問你需要多少步,把這個樹變成你需要的樹的顏色。
思路:本題的思路就是從根節點開始染色,把所有的子節點染成需要的顏色。
代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e4+10;
int vis[maxn], color[maxn];
int head[maxn], nx[maxn], e[maxn], tot = 0;
void add(int u, int v){
    e[tot] = v;
    nx[tot] = head[u];
    head[u] = tot++;
}
void dfs(int x, int cr){
     vis[x] = cr;
     for(int i = head[x]; i != -1; i = nx[i]){
        int t = e[i];
        dfs(t, cr);

     }
}
int main(){
    int n, v;
    memset(vis, 0, sizeof vis);
    memset(head, -1, sizeof head);
    scanf("%d", &n);
    for(int i = 2; i <= n; i++){
        scanf("%d", &v);
        add(v, i);
    }
    int step = 0;
    for(int i = 1; i <= n; i++)scanf("%d", &color[i]);
    for(int i = 1; i <= n; i++){//這個需要根據結果去推一下
        if(vis[i] == color[i])continue;
        else {
            step++;
            dfs(i, color[i]);
        }
    }
    cout<<step<<endl;
return 0;
}

或者:

#include<bits/stdc++.h>
using namespace std;
queue<int>q;
const int maxn = 1e4+10;
int vis[maxn], color[maxn];
int head[maxn], nx[maxn], e[maxn], tot = 0;
void add(int u, int v)
{
    e[tot] = v;
    nx[tot] = head[u];
    head[u] = tot++;
}
void dfs(int x, int cr)
{
    vis[x] = cr;
    for(int i = head[x]; i != -1; i = nx[i])
    {
        int t = e[i];
        dfs(t, cr);
    }
}
int main()
{
    int n, v;
    memset(vis, 0, sizeof vis);
    memset(head, -1, sizeof head);
    scanf("%d", &n);
    for(int i = 2; i <= n; i++)
    {
        scanf("%d", &v);
        add(v, i);
    }
    int step = 0;
    for(int i = 1; i <= n; i++)scanf("%d", &color[i]);
    q.push(1);
    while(!q.empty())
    {
        int t = q.front();
        for(int i = head[t]; i != -1; i = nx[i])
        {
            q.push(e[i]);
        }
        q.pop();
        if(vis[t] == color[t])continue;
        else
        {
            step++;
            dfs(t, color[t]);
        }
    }
    cout<<step<<endl;
    return 0;
}

題目:Two Buttons

由於是最短的所以bfs更好。
代碼:

#include<bits/stdc++.h>
using namespace std;
 
int n, m, ans = 0;
int vis[10010];
queue<int>q;
int dfs(int x)
{
    q.push(x);
    vis[x] = 0;
    while(q.size()){
        int t = q.front();
        q.pop();
        if(t == m)return vis[m];
        int xx = t * 2;
        if(xx <= 10000 && !vis[xx]){
            vis[xx] = vis[t] + 1;
            q.push(xx);
        }
        xx = t-1;
        if(xx > 0 && !vis[xx]){
            vis[xx] = vis[t] + 1;
            q.push(xx);
        }
 
    }
 
}
int main()
{
    scanf("%d %d", &n, &m);
    ans = 1e7;
    ans = 0;
    if(n > m)
    {
        cout<<n-m<<endl;
    }
    else
    {
        cout<<dfs(n)<<endl;
    }
 
    return 0;
}

題目: Ice Skating

思路:就是求出幾個互相聯通的集合,集合的數量減去1就是答案。
代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int x[maxn], y[maxn];
int n, vis[maxn];
void dfs(int i){
     vis[i] = 1;
     for(int j = 1; j <= n; j++){
        if(!vis[j] && (x[i] == x[j] || y[i] == y[j])){
            dfs(j);
        }
     }
}
int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        scanf("%d %d", &x[i], &y[i]);
    }
    int ans = 0;
    for(int i = 1; i <= n; i++){
        if(!vis[i])
        dfs(i), ans++;
    }
    printf("%d\n", ans-1);

return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章