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