第三周训练 | 搜索技术

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′|┛ 嗷~~

千万别“while(cin>>n)”这样写,直接超时

#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. 题意:有一个n*m的棋牌,给出k种颜色和每种颜色的数量。判断可不可以把他们涂色切相邻的颜色不一样。输出“YES”or"NO",和涂完色棋牌(多种方案只用输出一种就对)
  2. 一开始就要进行判断大剪枝,如果数量最多的颜色数大于所有方格的一半的时候就不可能了
  3. 而且应为我们是从左往右从上往下这样填的,所以只用判断左边和上面就行了
  4. 这里有一个特殊点,第一行和第一列和第一个点,这三点比较特殊需要单拎出来

#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;
} 

 

posted @ 2019-11-14 16:06  东坡肉肉君  阅读(161)  评论(0编辑  收藏  举报