• 第三周训练 | 搜索技术


    A - N皇后问题

    #include<iostream>
    #include<cmath>
    #include<string.h>
    using namespace std;
    int n,tot=0;
    int col[12]={0};
    bool check(int c,int r)
    {
        for(int i=0;i<r;++i)
        {
            if(col[i]==c||(abs(col[i]-c)==abs(i-r)))
            {
                return false;
            }
        }
        return true;
    }
    void DFS(int r)
    {
        if(r==n)
        {
            tot++;
            return;
        }
        for(int c=0;c<n;++c)
        {
            if(check(c,r))
            {
                col[r]=c;
                DFS(r+1);
            }
        }
    }
    int main()
    {
        int ans[12]={0};
        for(n=0;n<=10;++n)
        {
            memset(col,0,sizeof(col));
            tot=0;DFS(0);ans[n]=tot; 
        }    
        while(cin>>n)
        {
            if(n==0)return 0;
            cout<<ans[n]<<endl;
        }
        return 0;
    }

     B - Network Saboteur

    一开始WA了,检查了老半天原来是在dfs结束后,选中的值没有复位,这题的思路就是以第一个元素为基准点,其他的元素选择加入或者不加入它在的集合

    #include<iostream>
    #include<cmath>
    #include<string.h>
    #define SETLEN 22
    using namespace std;
    int node[22][22]={0};//行代表结点,列代表它到达其他结点的距离 
    //判断第r个点加入还是不加入0号点的集合
    //传入当前的集合,和当前的开销 
    int result=0; 
    int num=0;
    int DFS(int r,int set[],int tot) 
    {
        if(tot>result)result=tot;//更新最大值
        if(r==num)//所有的点都遍历完了就输出最大的开销 
        {
            return result;
        }
        for(int c=0;c<2;++c)
        {
            if(c)//如果加入 
            {
                set[r]=1;//加r号结点加入集合
                int temp=0;
                for(int i=0;i<SETLEN;++i) 
                {
                    if(set[i])//遍历一下集合中的点 
                    {
                        //找到不在集合里的点,查一下表,计算开销 
                        for(int j=0;j<SETLEN;++j)
                        {
                            if(!set[j]) 
                                temp+=node[i][j];
                        }
                        
                    }
                }
                DFS(r+1,set,temp);
                set[r]=0;//复位 
            }
            else//如果不加入 
            {
                int temp=0;
                for(int i=0;i<SETLEN;++i) 
                {
                    if(set[i])//遍历一下集合中的点 
                    {
                        //找到不在集合里的点,查一下表,计算开销 
                        for(int j=0;j<SETLEN;++j)
                        {
                            if(!set[j]) 
                                temp+=node[i][j];
                        }
                        
                    }
                }
                DFS(r+1,set,temp);
            }
        }
    
    }
    int main()
    {    
        int set[SETLEN]={};
        memset(set,0,sizeof(set));
        cin>>num;
        for(int i=0;i<num;++i)
        {
            for(int j=0;j<num;++j)
            {
                cin>>node[i][j];
            }
         } 
         set[0]=1;//默认0号点在集合里 
         DFS(1,set,0); 
         cout<<result;
        return 0;
    }

    真的是要改到吐血了,原来是39行少了一个flag=0;(假设有这种情况:在分割的时候第一次出现了——1,11,12,第二次分割的时候出现了——11,1,12,此时flag置为1,但是他们的和1+11+12=24是小于我要求的114(1+111+2),但是后来输出还是以rejected输出,这样就不对劲了)看来恢复很重要啊安吖

    #include<iostream>
    #include<cmath>
    #include<vector>
    #include<queue> 
    #include<stdlib.h>
    #include<cstring>
    #include<string>
    using namespace std;
    //判断当前这个结点选还是不选
    vector<int>set,pre_set,result;
    string str="";
    int tag=0;
    int flag=0;//判重标志 
    int pre_sum=0; 
    //int sum:当前的和
    //int now:当前切割形成的数 
    //int index:当前遍历的数的下标
    void inita()
    {
        set.clear();//存放切下来的数字
        pre_set.clear();
        result.clear();
        str=""; 
        tag=0;
        flag=0;//判重标志 
        pre_sum=0;
    } 
    void DFS(int sum,int now,int index) 
    {
        if(sum>tag||now>tag)return;//如果当前构成的数字大于目标值直接返回 
        if(index==str.length()-1) 
        {
            sum+=now;
            if(sum<=tag)
            {    
                pre_set.push_back(now);//将最后一个存起来
                if(pre_sum<sum)
                {
                    flag=0;
                    pre_sum=sum;
                    //跟新结果集
                    result.clear();
                    for(int i=0;i<pre_set.size();++i)
                    {
                        result.push_back(pre_set[i]);    
                    }     
                }
                else if(pre_sum==sum)
                {
                    flag=1;
                }    
                pre_set.pop_back();
            }
            
            return;
        
        }
        for(int c=0;c<2;++c)
        {
            if(c)//如果切 
            {    pre_set.push_back(now);//如果切了,将当前切割形成的数放到结果集里 
                DFS(sum+now,set[index+1],index+1); 
                pre_set.pop_back();       
            }
            else//如果不切 
            {
                DFS(sum,now*10+set[index+1],index+1);
            }
        }
    
    }
    int main()
    {    
        while(1)
        {
            inita();
            cin>>tag>>str;
            if(tag==0&&str=="0")break;
            
            int Sum=0;
            for(int i=0;i<str.length();++i)
            {
                Sum+=str[i]-'0';
                set.push_back(str[i]-'0');//将所有的字符分割 
            }
            if(Sum>tag)
            {
                cout<<"error"<<endl;
                continue;
            }
            
            DFS(0,set[0],0);
            
             if(flag)
            {
                cout<<"rejected"<<endl;
            }
            else
            {
                cout<<pre_sum;
                for(int i=0;i<result.size();++i)
                {
                    cout<<" "<<result[i];
                }    
                cout<<endl;
            }     
        }
        return 0;
    }

    D - Sudoku POJ - 2676

    最简单朴素的想法-——挨个式,一发超时心塞塞

    #include<iostream>
    #include<string.h>
    #include<string>
    #define Not_Safe(x,y) (x<=0||x>10||y<=0||y>10)
    using namespace std;
    int KeyBoard[10][10];
    int flag;
    int Intial()
    {
        //初始化函数
        memset(KeyBoard,0,sizeof(int)*100);
    }
    int Print()
    {
        for(int j=1;j<=9;++j)
        {
            cout<<KeyBoard[j][1];
            for(int k=2;k<=9;++k)
            {
                 cout<<" "<<KeyBoard[j][k];
            }
            cout<<endl;
        }
    }
    int CheckH(int num,int x)
    {
        for(int j=1;j<10;++j)
        {
            if(KeyBoard[x][j]==num)
            {
                return false;
            }
        }
        return true;
    }
    int CheckL(int num,int y)
    {
        for(int j=1;j<10;++j)
        {
            if(KeyBoard[j][y]==num)
            {
                return false;
            }
        }
        return true;
    }
    int CheckSs(int num,int x,int y)//传入的是小九宫的起点
    {
        for(int i=x;i<3+x;++i)
        {
            for(int j=y;j<3+y;++j)
            {
                if(KeyBoard[i][j]==num) return false;
    //               cout<<KeyBoard[i][j]<<" ";
            }
    
            cout<<endl;
        }
        return true;
    }
    int CheckS(int num,int x,int y)
    {
        int a=x/3;int b=y/3;
        int flag;
        if(a==0)
        {
            switch(b)
            {
                case 0:flag=CheckSs(num,1,1);break;
                case 1:flag=CheckSs(num,1,4);break;
                case 2:flag=CheckSs(num,1,7);break;
            }
        }
        else if(a==1)
        {
            switch(b)
            {
                case 0:flag=CheckSs(num,4,1);break;
                case 1:flag=CheckSs(num,4,4);break;
                case 2:flag=CheckSs(num,4,7);break;
            }
        }
        else
        {
            switch(b)
            {
                case 0:flag=CheckSs(num,7,1);break;
                case 1:flag=CheckSs(num,7,4);break;
                case 2:flag=CheckSs(num,7,7);break;
            }
        }
        return flag;
    
    }
    void DFS(int z)//传入的z从0开始编号,存的数据是从1开始
    {
        int x=z/9+1;
        int y=z%9+1;
        if(z>81)return;
        if(Not_Safe(x,y))return;
        if(!KeyBoard[x][y])//如果这个位置上为0
        {
            //******************************************
            cout<<"z="<<z<<endl;
            Print();
            //******************************************
    
            for(int i=1;i<=9;++i)//遍历这个位置可以填的数
            {
                if(CheckS(i,x,y)&&CheckL(i,y)&&CheckH(i,x))
                {
                    KeyBoard[x][y]=i;
                    DFS(z+1);
                    KeyBoard[x][y]=0;
                }
            }
        }
        else
        {
            DFS(z+1);
        }
    
    }
    int main()
    {
        int n;
        string temp="";
        cin>>n;
        for(int i=0;i<n;++i)
        {
            //输入9x9的棋盘
            for(int j=1;j<=9;++j)
            {
                cin>>temp;
                for(int k=1;k<=9;++k)
                {
                     KeyBoard[j][k]=temp[k-1]-'0';
                }
            }
    //        cout<<"棋盘输入完毕"<<endl;
    //       CheckSs(6,1,4);
            DFS(0);
            Print();
            Intial();
        }
        return 0;
    }
    View Code

     一直WA原来是格式错了,两个数之间不要有空格,借鉴了一波大佬的写法,用桶排的思想,建立二维数组去重,这样查找的时候就是O(1)比我的O(81)快的多┗|`O′|┛ 嗷~~

    #include<iostream>
    #include<string.h>
    #include<string>
    #define Not_Safe(x,y) (x<=0||x>10||y<=0||y>10)
    #define CheckSs(x,y) (3*((x-1)/3)+(y-1)/3+1)
    using namespace std;
    int KeyBoard[10][10];
    int CheckH[10][10];//第一个下标代表这一行的行号,第二个代表这一行出现的数字
    int CheckL[10][10];//第一个下标代表这一列的列号,第二个代表这一列出现的数字
    int CheckS[10][10];//第一个下标代表小九宫的工号,第二个代表小九宫出现的数字
    int flag;
    void Intial()
    {
        //初始化函数
        memset(KeyBoard,0,sizeof(int)*100);
        memset(CheckH,0,sizeof(int)*100);
        memset(CheckL,0,sizeof(int)*100);
        memset(CheckS,0,sizeof(int)*100);
        flag=0;
    }
    void Print(int a[10][10])
    {
        for(int j=1;j<=9;++j)
        {
            for(int k=1;k<=9;++k)
            {
                 cout<<a[j][k];
            }
            cout<<endl;
        }
    }
    void DFS(int z)//传入的z从0开始编号,存的数据是从1开始
    {
        int x=z/9+1;
        int y=z%9+1;
        if(z>=81)
        {
            Print(KeyBoard);
            flag=1;
            return;
        }
        if(flag)return;
        if(Not_Safe(x,y))return;
        if(!KeyBoard[x][y])//如果这个位置上为0
        {
    
            for(int i=1;i<=9;++i)//遍历这个位置可以填的数
            {
                if(!CheckS[CheckSs(x,y)][i]&&!CheckL[y][i]&&!CheckH[x][i])
                {
                    KeyBoard[x][y]=i;
                    CheckS[CheckSs(x,y)][i]=1;
                    CheckL[y][i]=1;
                    CheckH[x][i]=1;
    //                ******************************************
    //                cout<<"z="<<z<<endl;
    //                Print(KeyBoard);
    //                ******************************************
                    DFS(z+1);
    
                    KeyBoard[x][y]=0;
                    CheckS[CheckSs(x,y)][i]=0;
                    CheckL[y][i]=0;
                    CheckH[x][i]=0;
                }
            }
        }
        else
        {
            DFS(z+1);
        }
    
    }
    int main()
    {
        int n;
        string temp="";
        cin>>n;
        for(int i=0;i<n;++i)
        {
            //输入9x9的棋盘
            for(int j=1;j<=9;++j)
            {
                cin>>temp;
                for(int k=1;k<=9;++k)
                {
                     KeyBoard[j][k]=temp[k-1]-'0';
                     CheckH[j][KeyBoard[j][k]]=1;
                     CheckL[k][KeyBoard[j][k]]=1;
                     CheckS[CheckSs(j,k)][KeyBoard[j][k]]=1;//从1开始编号
    
                }
            }
    
    //        cout<<"棋盘输入完毕"<<endl;
            DFS(0);
            temp="";
            Intial();
        }
        return 0;
    }

     感谢大悲咒

    #include<iostream>
    #include<string.h>
    #include<string>
    #define MAX 1002
    #define Not_Safe(x,y,n,m) (x<=0||x>n||y<=0||y>m)
    #define CheckSs(x,y) (3*((x-1)/3)+(y-1)/3+1)
    using namespace std;
    //记录一下上一次走的方向,如果现在的选择方向跟上一次不一样就代表骨折一次
    //***************************************************
    //                cout<<"这一次的方向"<<i<<",上一回的方向:"<<lastdir<<",这一次的遍历点:("<<newa.x<<","<<newa.y<<"),剩余转折次数:"<<COUNT<<endl;
    //                Print();
    //                //***************************************************
    typedef struct cc
    {
        int x;
        int y;
    }Pos;
    int KeyBoard[MAX][MAX];
    int flag,n,m,c;
    Pos a,b;
    int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
    void Intial()
    {
        //初始化函数
        memset(KeyBoard,0,sizeof(int)*MAX);
        flag=0;
        n=0,m=0,c=0;
        a.x=0,a.y=0;
        b.x=0,b.y=0;
    }
    void Print()
    {
    
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                cout<<KeyBoard[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    void DFS(Pos a,int lastdir,int COUNT)//传入的棋子的起点坐标
    {
    
        if(flag)return;
        if(COUNT>=3)return;
        if(Not_Safe(a.x,a.y,n,m))return;
        if(a.x==b.x&&a.y==b.y)
        {
            flag=1;return;
        }
        if(KeyBoard[a.x][a.y])return;
        if(COUNT==2)
        {
            switch(lastdir)
            {
                case 0:if(!(a.y==b.y&&a.x>b.x)) return;break;
                case 1:if(!(a.y==b.y&&a.x<b.x)) return;break;
                case 2:if(!(a.y>b.y&&a.x==b.x)) return;break;
                case 3:if(!(a.y<b.y&&a.x==b.x)) return;break;
            }
        }
        if(KeyBoard[a.x][a.y]==-1)return;
        int temp=KeyBoard[a.x][a.y];
        KeyBoard[a.x][a.y]==-1;
        for(int i=0;i<4;++i)
        {
            Pos newa;
            newa.x=a.x+dir[i][0];
            newa.y=a.y+dir[i][1];
            if(lastdir==i)
            {
                DFS(newa,i,COUNT);
            }
            else
            {
                DFS(newa,i,COUNT+1);
            }
        }
        KeyBoard[a.x][a.y]==temp;
    }
    int main()
    {
        while(1)
        {
            Intial();
            cin>>n>>m;
            if(!n&&!m)break;
            for(int i=1;i<=n;++i)
            {
                //输入连连看的棋盘
                for(int j=1;j<=m;++j)
                {
                    cin>>KeyBoard[i][j];
                }
            }
            cin>>c;
            for(int i=0;i<c;++i)
            {
                flag=0;
                cin>>a.x>>a.y>>b.x>>b.y;
                if(a.x==b.x&&a.y==b.y&&!KeyBoard[a.x][a.y])
                {
                    cout<<"NO"<<endl;
                }
                else if(KeyBoard[a.x][a.y]==KeyBoard[b.x][b.y]&&KeyBoard[a.x][a.y])
                {
                    Pos newa;
                    for(int i=0;i<4;++i)
                    {
                        newa.x=a.x+dir[i][0];
                        newa.y=a.y+dir[i][1];
                        DFS(newa,i,0);
                        if(flag)break;
                    }
                    if(flag)
                    {
                        cout<<"YES"<<endl;
                    }
                    else
                    {
                        cout<<"NO"<<endl;
                    }
                }
                else
                {
                    cout<<"NO"<<endl;
                }
            }
    
        }
        return 0;
    }
    1. 一开始就要进行判断大剪枝,如果数量最多的颜色数大于所有方格的一半的时候就不可能了
    2. 而且应为我们是从左往右从上往下这样填的,所以只用判断左边和上面就行了
    3. 这里有一个特殊点,第一行和第一列和第一个点,这三点比较特殊需要单拎出来

    #include<iostream>
    #include<vector>
    #include<string.h>
    #include<algorithm>
    #define LEN 6
    #define Safe(x,y) (x>=0&&y>=0&&x<n&&y<m)
    using namespace std;
    int t,n,m,k,flag=0;
    int KeyBoard[LEN][LEN];
    struct c
    {
        int color;
        int n; 
        bool operator < (const c &i) const   
        {
            return n > i.n;
        }
    }color[30]; 
    void intial()
    {
        flag=0;
        memset(color,0,sizeof(c)*30);
        memset(KeyBoard,0,sizeof(int)*36);
    }
    void DFS(int z)
    {
        if(flag)return;
        if(z>=m*n)
        {
            flag=1;return;
        }    
        int x=z/m;
        int y=z%m;
        if(!Safe(x,y))return;
        for(int i=1;i<=k;++i)
        {    
            if(color[i].n&&!flag)
            {    
                if(x!=0&&y!=0)
                { 
                    if(color[i].color==KeyBoard[x][y-1]||color[i].color==KeyBoard[x-1][y])continue;
                }
                else if(x==0&&y!=0)
                {
                    if(color[i].color==KeyBoard[x][y-1])continue;
                }
                else if(x!=0&&y==0)
                {
                    if(color[i].color==KeyBoard[x-1][y])continue;
                }
                KeyBoard[x][y]=color[i].color;
                --color[i].n;
                DFS(z+1);
                ++color[i].n;                
            }
        }
        return;
    }
    int main()
    {
        cin>>t;
        for(int i=0;i<t;++i)
        {
            intial();
            cin>>n>>m>>k;
            for(int j=1;j<=k;++j)
            {            
                cin>>color[j].n;
                color[j].color=j;
            }
            sort(color+1,color+k+1);
           
            if(color[1].n<=(n*m+1)/2)
            {
                DFS(0);
            }
            cout<<"Case #"<<i+1<<":"<<endl;
            if(flag)
            {
                cout<<"YES"<<endl;
                for(int i=0;i<n;++i)
                {
                    cout<<KeyBoard[i][0];
                    for(int j=1;j<m;++j)
                    {
                        cout<<" "<<KeyBoard[i][j];
                    }
                    cout<<endl;
                }
            }
            else
            {
                cout<<"NO"<<endl;
            }
        }
        return 0;
    }

    E - Channel Allocation POJ - 1129 

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<vector>
    #define MAX 27
    using namespace std;
    typedef struct s
    {
        int num;
        int color;
    }Color;
    vector<Color>c[MAX];
    Color mark[MAX];
    int n=0;
    string temp;
    int vis[MAX];
    void intial()
    {
        memset(vis,0,sizeof(int)*4);
        memset(mark,0,sizeof(Color)*MAX);
        for(int i=0;i<MAX;++i)c[i].clear();    
        temp="";
    }
    bool End()
    {
        for(int i=0;i<n;++i)
        {
            if(!mark[i].color)return false;
        }
        return true;
    }
    int max;
    void dfs(int index)
    {
        if(index==n)return;//如果遍历完最后一个就结束
        if(End())return;
        if(c[index].size()!=1)
        {    
            int Color_num[5]={};
            for(int i=1;i<c[index].size();++i)
            {
                
                if(mark[c[index][i].num].color) 
                {
                    Color_num[mark[c[index][i].num].color]=1;
                }
                //找一下周围的颜色有哪些,然后把它们去除 
            }
            for(int i=1;i<=4;++i)
            {
                if(!Color_num[i])
                {
                    if(End())return;
                    mark[c[index][0].num].color=i;//涂色                         
                    dfs(index+1);
                }
            }
        }
        else
        {
            if(End())return;
            dfs(index+1);
        }
        return;
    }
    int main()
    {
        while(cin>>n&&n)
        {
            intial();
            for(int j=0;j<n;++j)
            {
                cin>>temp;
                Color now;now.num=temp[0]-'A';
                c[j].push_back(now);//数组第一位放头 
                mark[now.num].num=now.num; 
                for(int i=2;i<temp.length();++i)
                {
                    now.num=temp[i]-'A';
                    c[j].push_back(now);
                }    
            }
            dfs(0);//传入数组第一个
            int count=mark[0].color;
            for(int i=1;i<n;++i)
            {
                if(count<mark[i].color)count=mark[i].color;
            } 
            if(count)
            {
                if(count==1)
                {
                    cout<<"1 channel needed."<<endl; 
                }
                else
                {
                    cout<<count<<" channels needed."<<endl; 
                }    
            }
            else
            {
                if(n==2)
                {
                    cout<<"1 channel needed."<<endl; 
                }
                if(n==3)
                {
                    cout<<"3 channels needed."<<endl; 
                }
                if(n>=4)    
                {
                    cout<<"4 channels needed."<<endl; 
                }
            }        
        }
        
        return 0;
    } 
  • 相关阅读:
    python限定方法参数类型、返回值类型、变量类型等
    双拼自然码
    关于将汉语拼音字母“ü”改成“v”的设想和建议
    数据库转模型图
    python中的捕获异常、异常跟踪
    内部教师爆料:某些民办学校真正的内幕
    炸薯条
    IntelliJ IDEA添加JavaDOC注释 方法 快捷键
    java获取当前路径的方法
    java获取全部子类或接口的全部实现
  • 原文地址:https://www.cnblogs.com/chrysanthemum/p/11858033.html
Copyright © 2020-2023  润新知