1 P1219 [USACO1.5]八皇后 Checker Challenge
#c++搜索与回溯
##基本思路:搜索 标记 AC
###注释:对角线d[i-j]后面必须加上一个n,因为i-j可能为负数,那么数组就会出错,所以将整体向右偏移n个单位(坐标偏移不会影响我们需要达到的目的),将所有可能变成正数;(因为i-j的最小值是-n+1,所以加上一个n就一定会变成一个正数)
本道题最重要的就是记录下皇后占领的格子(打标记的思想),通过此判断下一个皇后是否可以在某个位置,如果可以,则继续搜索下一个皇后可以在的位置,如果不行,则清除标记回到上一步,继续搜索;
可以先考虑六个皇后(即6*6网格),再将6改为n,并且输入n,就可以得出6到13个皇后的解了。
#include<bits/stdc++.h>
using namespace std;
int a[100],b[100],c[100],d[100];
/*
a数组表示的是行;
b数组表示的是列;
c数组表示的是左下到右上的对角线;
d数组表示的是左上到右下的对角线;
*/
int total;//总数:记录解的总数
int n;//输入的数,即N*N的格子,全局变量,搜索中要用
void print(){
if(total<=2){//保证只输出前三个解,如果解超出三个就不再输出,但后面的total还需要继续叠加
for(int k=1;k<=n;k++)
cout<<a[k]<<" ";//for语句输出
cout<<endl;
}
total++;//total既是总数,也是前三个排列的判断
}
void queen(int i){//搜索与回溯主体
if(i>n){
print();
return;
}else{
for(int j=1;j<=n;j++){//尝试可能的位置
if(!b[j] && !c[i+j] && !d[i-j+n]){//如果没有皇后占领,执行以下程序
a[i]=j;//标记i排是第j个
b[j]=1;//宣布占领纵行
c[i+j]=1;
d[i-j+n]=1;
//宣布占领两条对角线
queen(i+1);//进一步搜索,下一个皇后
b[j]=0;
c[i+j]=0;
d[i-j+n]=0;
//(回到上一步)清除标记
}
}
}
}
int main(){
cin>>n;//输入N*N网格,n已在全局中定义
queen(1);//第一个皇后
cout<<total<<endl;//输出可能的总数
return 0;
}
2 P2392 kkksc03考前临时抱佛脚
枚举每道题交给哪边的脑子解决,找到两边时间较大值的最小值。
对于一道题只有两个状态,一是加到左脑,二是加到右脑,所以是01背包。这里还可以用另一个思想,将一边的脑子加到最接近一半则另一边脑子时间就是正解。
//搜索解法
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x7fffffff;
int Left,Right,minn,ans;
int s[5],a[21][5];
void search(int x,int y){
if(x>s[y]){
minn=min(minn,max(Left,Right));
return;
}
Left+=a[x][y];
search(x+1,y);
Left-=a[x][y];
Right+=a[x][y];
search(x+1,y);
Right-=a[x][y];//搜索回溯
}
int main(){
ios::sync_with_stdio(false);
for(int i=1;i<=4;i++) cin>>s[i];
for(int i=1;i<=4;i++){
Left=Right=0;
minn=INF;
for(int j=1;j<=s[i];j++) cin>>a[j][i];
search(1,i);
ans+=minn;
}
cout<<ans<<"\n";
return 0;
}
//DP解法
#include<bits/stdc++.h>
using namespace std;
int a[5],sum,t,homework[21],dp[2501];
int main(){
for(int i=1;i<=4;i++) cin>>a[i];
for(int i=1;i<=4;i++){
sum=0;
memset(dp,0,sizeof(dp));
for(int j=1;j<=a[i];j++){
cin>>homework[j];
sum+=homework[j];
}
for(int j=1;j<=a[i];j++){
for(int k=sum/2;k>=homework[j];k--)//只要是总和的一半
dp[k]=max(dp[k],dp[k-homework[j]]+homework[j]);//01背包
}
t+=sum-dp[sum/2];//累加为另一个脑子
}
cout<<t<<endl;
return 0;
}
3 P1443 马的遍历
用queue写bfs
#include<bits/stdc++.h>
using namespace std;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};//8个方向
queue<pair<int,int> > q;
int f[500][500];//存步数
bool vis[500][500];//判断走没走过
int n,m,x,y;
int main(){
memset(f,-1,sizeof(f)); memset(vis,false,sizeof(vis));
cin>>n>>m>>x>>y;
f[x][y]=0; vis[x][y]=true;
q.push(make_pair(x,y));
while(!q.empty()){
int xx=q.front().first,yy=q.front().second;
q.pop();//取队首并出队
for(int i=0;i<8;i++){
int u=xx+dx[i],v=yy+dy[i];
if(u<1||u>n||v<1||v>m||vis[u][v]) continue;//出界或走过了就不走
vis[u][v]=true;
q.push(make_pair(u,v));
f[u][v]=f[xx][yy]+1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%-5d",f[i][j]);
}
printf("\n");
}
return 0;
}
4 P1135 奇怪的电梯
这题和上题3 P1443 马的遍历思想一致。
#include<bits/stdc++.h>
using namespace std;
const int N=200+10;
const int dx[2]={1,-1};
queue<int> q;
int n,a,b,K[N];
int f[N]; //存步数
bool vis[N]; //判断走过没
int main(){
memset(f,-1,sizeof(f)); memset(vis,false,sizeof(vis));
cin>>n>>a>>b;
for(int i=1;i<=n;i++) cin>>K[i];
f[a]=0; vis[a]=true;
q.push(a);
while(!q.empty()){
int xx=q.front();
q.pop();
for(int i=0;i<2;i++){
int u=xx+K[xx]*dx[i];
if(u<1||u>n||vis[u]) continue;
vis[u]=true;
q.push(u);
f[u]=f[xx]+1;
}
}
cout<<f[b]<<endl;
return 0;
}
5 P2895 [USACO08FEB]Meteor Shower S
#include<bits/stdc++.h>
using namespace std;
const int dx[5]={0,0,0,1,-1};
const int dy[5]={0,1,-1,0,0};//方便移动和处理陨石砸落
queue<pair<int,int> > q;//构造队列,存储将处理点x,y坐标
int n,ma[305][305],v[305][305],sx,sy,st,ans[305][305];
/*n:陨石数量 ma:陨石砸落地图 v:记录是否走过地图
sx,sy,st:陨石x,y坐标及砸落时间 ans:每个点的最少时间图*/
int read(){
int x=0,y=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') y=-y;
for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
return x*y;
}
void write(int x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
int ch(int a){
if(a==-1) return 99999;
else return a;
}//判断路过该点时是否陨石已经砸落,如果是没有陨石,相当于n年后砸落
int main(){
n=read();
memset(ma,-1,sizeof(ma));//陨石初始化为-1
for(int i=1;i<=n;i++){
sx=read();sy=read();st=read();//输入陨石
for(int j=0;j<5;j++){//上下左右中标记陨石
if(sx+dx[j]>=0&&sy+dy[j]>=0&&(ma[sx+dx[j]][sy+dy[j]]==-1||ma[sx+dx[j]][sy+dy[j]]>st)){
//如果该标记x,y坐标大于0且该点没有被陨石砸落或已标记时间没有该时间早,标记陨石
ma[sx+dx[j]][sy+dy[j]]=st;
}
}
}
v[0][0]=1;//初始点设为已走过
q.push(make_pair(0,0));//初始点放入队列
while(!q.empty()){//只要队列不为空
int x=q.front().first,y=q.front().second;//提取将处理点x,y坐标
q.pop();
int s=ans[x][y]+1;//即将标记的点时间是现在点的下一个单位
if(ma[x][y]==-1){//如果该点安全,输出即将标记的点的时间-1
write(s-1);
puts("");
return 0;
}
for(int i=1;i<=4;i++){
int xx=x+dx[i],yy=y+dy[i];//提取将处理点的坐标
if(xx>=0&&yy>=0&&s<ch(ma[xx][yy])&&v[xx][yy]==0){
//将处理点需要x,y坐标大于等于0且该点没有走过并且陨石降落时间小于现时间
q.push(make_pair(xx,yy));//放入将处理队列
v[xx][yy]=1;//标记已经走过
ans[xx][yy]=s;//将该点时间放入数组
}
}
}
puts("-1\n");//如果出不了陨石区,输出-1
return 0;
}