继上次博客到今天也有好几天了,期间懈怠了点,昨天又因为一些原因肚子疼所以一个晚上没有刷题,愧疚。这次写了几个稍微难一点的搜索,也算学到了点知识吧。不多说,上题。
HDU-1429 胜利大逃亡(续)
题目大意就是,地牢里有一些门,相应的有一些钥匙,问你能否走出地牢。利用状态压缩就可以解决了。不过状压还是看了别人的才知道怎么用的。果然智商不够用啊,唉。
状压+bfs,用“或运算”来收取钥匙,“与运算”来开门。
代码清单:
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
int x,y;
int step; //记录步数
int key; //二进制表示所拥有的钥匙
};
int n,m,t,key;
int sx,sy,ex,ey;
char s[25][25];
int vis[25][25][2500];
int xy[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
void bfs()
{
queue<edge>q;
edge p,w;
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));
p.x=sx; p.y=sy; p.step=0; p.key=0;
vis[p.x][p.y][p.key]=1;
q.push(p);
while(q.size()){
p=q.front(); q.pop();
if(p.x==ex&&p.y==ey){
printf("%d\n",p.step);
return ;
}
for(int i=0;i<4;i++){
w.key=p.key;
w.step=p.step+1;
w.x=p.x+xy[i][0];
w.y=p.y+xy[i][1];
if(w.x>=0&&w.x<n&&w.y>=0&&w.y<m&&!vis[w.x][w.y][w.key]&&s[w.x][w.y]!='*'&&w.step<t){
if(s[w.x][w.y]>='a'&&s[w.x][w.y]<='j'){ //如果是钥匙
key=1<<(s[w.x][w.y]-'a');
if(!vis[w.x][w.y][w.key|key]){
w.key=w.key|key; //或运算,把钥匙收入key中
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
else if(s[w.x][w.y]>='A'&&s[w.x][w.y]<='J'){
key=1<<(s[w.x][w.y]-'A');
if(w.key&key){ //判断有没有相应的钥匙
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
else{
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
}
}printf("-1\n");
}
int main()
{
while(scanf("%d%d%d",&n,&m,&t)!=EOF){
for(int i=0;i<n;i++){
scanf("%s",s[i]);
for(int j=0;j<m;j++){
if(s[i][j]=='@'){
sx=i;
sy=j;
}
if(s[i][j]=='^'){
ex=i;
ey=j;
}
}
}
bfs();
}return 0;
}
HDU-1885 Key Task
跟上面那个吧题目差不多,稍微改一下就可以。
状压+bfs。
代码清单:
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>]
using namespace std;
struct edge
{
int x,y;
int step;
int key;
};
int C,R,key;
int sx,sy,check;
char s[205][205];
int vis[205][205][20];
int xy[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
bool check1(char c)
{
if(c=='b'||c=='y'||c=='r'||c=='g') return true;
return false;
}
bool check2(char c)
{
if(c=='B'||c=='Y'||c=='R'||c=='G') return true;
return false;
}
void bfs()
{
queue<edge>q;
edge p,w;
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));
map<char,int>m;
m.clear(); //map初始化
m['b']=m['B']=0;
m['y']=m['Y']=1;
m['r']=m['R']=2;
m['g']=m['G']=3;
p.x=sx; p.y=sy; p.step=0; p.key=0;
vis[p.x][p.y][p.key]=1;
q.push(p);
while(q.size())
{
p=q.front(); q.pop();
if(s[p.x][p.y]=='X')
{
printf("Escape possible in %d steps.\n",p.step);
return;
}
for(int i=0;i<4;i++)
{
w.x=p.x+xy[i][0];
w.y=p.y+xy[i][1];
w.key=p.key;
w.step=p.step+1;
if(w.x>=0&&w.x<C&&w.y>=0&&w.y<R&&!vis[w.x][w.y][w.key]&&s[w.x][w.y]!='#')
{
if(check1(s[w.x][w.y]))
{
key=1<<m[s[w.x][w.y]];
if(!vis[w.x][w.y][w.key|key])
{
w.key=w.key|key;
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
else if(check2(s[w.x][w.y]))
{
key=1<<m[s[w.x][w.y]];
if(w.key&key)
{
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
else
{
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
}
}printf("The poor student is trapped!\n");
}
int main()
{
while(scanf("%d%d",&C,&R)!=EOF)
{
if(C==0&&R==0) break;
check=1;
for(int i=0;i<C;i++)
{
scanf("%s",s[i]);
for(int j=0;j<R;j++)
{
if(s[i][j]=='*')
{
sx=i;
sy=j;
}
if(s[i][j]=='X')
{
check=0;
}
}
}
if(check) printf("The poor student is trapped!\n");
else bfs();
}return 0;
}
HDU-4771 Stealing Harry Potter's Precious
这是今天第一个1A的题目。题目意思也是很简单,关键是要想到用状态压缩来解决。题目给出的K最多只有4,也就是相当于最多只有4把钥匙一样,我们的目的就是如何用最短的步骤数拿到这K把钥匙。我把钥匙用A,B,C,D表示。然后题目就简单了,和上面的题目一个意思。
状压+bfs。
代码清单:
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
struct edge{
int x,y;
int key;
int step;
};
int N,M,x,y,k;
int sx,sy,key,sum,tot;
int vis[105][105][20];
char s[105][105],str;
int xy[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
bool check(int xx,int yy){
if(xx>=0&&xx<N&&yy>=0&&yy<M) return true;
return false;
}
void bfs(){
queue<edge>q;
edge p,w;
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));
p.x=sx; p.y=sy; p.step=0; p.key=0;
vis[p.x][p.y][p.key]=1;
q.push(p);
while(q.size()){
p=q.front(); q.pop(); //cout<<p.x<<" "<<p.y<<endl;
if(p.key==sum){
printf("%d\n",p.step);
return ;
}
for(int i=0;i<4;i++){
w.x=p.x+xy[i][0];
w.y=p.y+xy[i][1];
w.key=p.key;
w.step=p.step+1;
if(check(w.x,w.y)&&!vis[w.x][w.y][w.key]&&s[w.x][w.y]!='#'){
if(s[w.x][w.y]>='A'&&s[w.x][w.y]<='Z'){
key=1<<(s[w.x][w.y]-'A');
if(!vis[w.x][w.y][w.key|key]){
w.key=w.key|key;
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
else{
vis[w.x][w.y][w.key]=1;
q.push(w);
}
}
}
}
printf("-1\n");
}
int main()
{
while(scanf("%d%d",&N,&M)!=EOF){
if(N==0&&M==0) break;
for(int i=0;i<N;i++){
scanf("%s",s[i]);
for(int j=0;j<M;j++){
if(s[i][j]=='@'){
sx=i;
sy=j;
}
}
}
scanf("%d",&k);
str='A'; sum=0; tot=1;
while(k--){
scanf("%d%d",&x,&y);
s[x-1][y-1]=str;
str++;
sum+=tot; //sum表示拿到全部钥匙之后状压二进制所表示的值
tot*=2;
}
bfs();
}return 0;
}
HDU-1226 超级密码
这道题刚开始看错了题目理解错了意思,后来看了题解恍然大悟啊。剁手啊看题解!!!不过从题解中也学到了很多,比如string的一些用法,这么神奇我以前居然不知道。
其实这道题最关键的是要想到:十进制数N<=5000,也就是说,找到的密码对N取余最多有5000种可能,所以对余数进行标记。剩下的就是自己的事了。不多说,上代码。
代码清单:
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
struct edge{
string ss;
int mod; //记录余数
};
int T,N,C,M,t;
int K,an,first,judge;
int vis[5005]; //对余数进行标记
string ans; //记录答案
char str[50],s[20];
void bfs(){
queue<edge>q;
edge p,w;
while(q.size()) q.pop();
memset(vis,0,sizeof(vis));
for(int i=0;i<K;i++){
if(s[i]=='0') continue;
p.ss=""; //赋初值为空
if(s[i]>='1'&&s[i]<='9') an=s[i]-'0';
else an=s[i]-'A'+10;
p.mod=an%N;
p.ss+=s[i];
vis[p.mod]=1;
q.push(p);
}
judge=0; ans="";
while(q.size()){
p=q.front(); q.pop();
if(judge&&p.ss.size()>ans.size()) continue; //如果字符串长度比当前记录的最小密码还大就不用继续进行下面步骤了
if(p.mod==0){ //如果能够整除,即余数为 0
if(!judge){ //找到第一个能够整除的密码,注意,不一定是最小的密码
judge=1;
ans=p.ss;
continue;
}
else{
if((p.ss.size()<ans.size()) || (p.ss.size()==ans.size()&&p.ss<ans)){ //如果密码小于当前记录的最小密码
ans=p.ss;
continue;
}
}
}
for(int i=0;i<K;i++){
if(s[i]>='0'&&s[i]<='9') an=s[i]-'0';
else an=s[i]-'A'+10;
w.ss=p.ss+s[i];
w.mod=(p.mod*C+an)%N; //这个是关键,求余数
if(!vis[w.mod]&&w.ss.size()<=500 || w.mod==0){ //入队条件
vis[w.mod]=1;
q.push(w);
}
}
}
if(!ans.size()) printf("give me the bomb please\n"); //如果字符串仍然为空,即不存在
else cout<<ans<<endl;
}
int main()
{
t=0;
scanf("%d",&T);
while(T--){
if(t>0) getchar();
scanf("%d %d",&N,&C);
getchar();
scanf("%d",&M);
getchar();
gets(str);
K=0; first=0;
for(int i=0;i<strlen(str);i++){
if(str[i]!=' ') s[K++]=str[i];
if(str[i]=='0') first=1;
}
if(N==0){ //如果N=0,那么只有0是它的倍数
if(first) printf("0\n");
else printf("give me the bomb please\n");
}
else{
sort(s,s+K); //从小到大排序,因为是找最小值
bfs();
}
t++;
}return 0;
}
好了,今天就写到这儿了,这几天运动会,该好好刷刷题了,时间也不多了,争取能有更大的进步吧!fighting!!!
永不丧失对生活的热情,人生就是要不停地战斗!