昨天做完最后一题就已经1点了,所以就连着今天的题目一起更。
Prime Path POJ - 3126
The ministers of the cabinet were quite upset by the message from the Chief of Security stating that they would all have to change the four-digit room numbers on their offices.
— It is a matter of security to change such things every now and then, to keep the enemy in the dark.
— But look, I have chosen my number 1033 for good reasons. I am the Prime minister, you know!
— I know, so therefore your new number 8179 is also a prime. You will just have to paste four new digits over the four old ones on your office door.
— No, it’s not that simple. Suppose that I change the first digit to an 8, then the number will read 8033 which is not a prime!
— I see, being the prime minister you cannot stand having a non-prime number on your door even for a few seconds.
— Correct! So I must invent a scheme for going from 1033 to 8179 by a path of prime numbers where only one digit is changed from one prime to the next prime.Now, the minister of finance, who had been eavesdropping, intervened.
— No unnecessary expenditure, please! I happen to know that the price
of a digit is one pound. — Hmm, in that case I need a computer
program to minimize the cost. You don’t know some very cheap software
gurus, do you? — In fact, I do. You see, there is this programming
contest going on… Help the prime minister to find the cheapest prime
path between any two given four-digit primes! The first digit must be
nonzero, of course. Here is a solution in the case above.1033 1733 3733 3739 3779 8779 8179 The cost of this solution is
6 pounds. Note that the digit 1 which got pasted over in step 2 can
not be reused in the last step – a new 1 must be purchased.
这道题的大意是一个首相闲着无聊找乐子,给出两个数,使得第一个数可以通过若干次变换,每次变换只能改变一个数字,最后变成第二个数,这变换过程每一个数字都必须是素数。
既然他要求是素数,而且查找量有些大,那我们可以打表,先预处理1000-10000之间的数,找到素数并标记。然后我们就用bfs找到到达第二个数的最少次数,在压入栈的时候,这里我们需要对4位数字都进行遍历,需要注意的是,千位数字不能为0,个位数字我们也可以判断只能是奇数,所以可以直接+=2;如果他是素数并且没有压入栈过,那么我们就把它压入栈。当到达第二个数字的时候就返回,这里我们需要知道的是,在队列里,是先进先出,所以第一个达到的肯定就是最短次数。详细代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<vector>
#define INF 0x3f3f3f3f
# define LOCAL
using namespace std;
const int maxn = 10005;
const int N = 10;
int a[maxn];//保存变换次数,下标是数字
int d[N]={0,1,2,3,4,5,6,7,8,9};//数字变化
int x,y,n;
bool is_prime[maxn];//判断是否是素数
bool vis[maxn];//判断是否压入栈过
void prime(){
memset(is_prime,0,sizeof(is_prime));
for(int i=1000;i<=maxn;i++){
int p = sqrt(i);
for(int j=2;j<=p;j++)
if(i%j==0){
is_prime[i] = 1;
break;
}
}
}//预处理数组,要用的时候直接查表
void bfs(){
queue<int> q;
q.push(x);
vis[x] = true;
a[x] = 0;
while(!q.empty()){
int temp = q.front();
q.pop();
if(temp == y){
cout << a[y] << endl;
return;
}
int k;
for(int i=1;i<N;i++){//千位,千位不能为0
k = temp%1000 + i*1000;
if(!is_prime[k] && !vis[k]){
q.push(k);
vis[k] = true;
a[k] = a[temp] + 1;
}
}
for(int i=0;i<N;i++){//百位
k = temp/1000 * 1000 + temp%100 + i*100;
if(!is_prime[k] && !vis[k]){
q.push(k);
vis[k] = true;
a[k] = a[temp] + 1;
}
}
for(int i=0;i<N;i++){//十位
k = temp/100 * 100 + temp%10 + i*10;
if(!is_prime[k] && !vis[k]){
q.push(k);
vis[k] = true;
a[k] = a[temp] + 1;
}
}
for(int i=1;i<N;i+=2){//个位,个位只能是奇数
k = temp/10 * 10 + i;
if(!is_prime[k] && !vis[k]){
q.push(k);
vis[k] = true;
a[k] = a[temp] + 1;
}
}
}
cout << "Impossible" << endl;//没有到达最后
return;
}
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
cin >> n;
prime();//预处理
while(n--){
cin >> x >> y;
memset(vis,false,sizeof(vis));//初始化
bfs();
}
return 0;
}
Shuffle’m Up POJ - 3087
这道题的大意是给三个字符串,其中前面两个字符串先按照第二个字符串最下的字符在新字符串最下面,最上面的字符是第一个字符串的最下的字符,类似交替排序,如果和第三个字符串不相同,则将下面一半作为新的s1,上面一半作为新的s2,再继续上面的操作,直到和第三个字符串一样。
这里我们需要注意的是,如果我们直接这样设计程序会陷入死循环,因为如果到不了第三个字符串的序列,到一定次数的时候,新字符串会重复出现。所以我们需要记录出现过的每一个新字符串,然后每次都对新字符串进行判断,如果有相同的,则证明无法到达第三个字符串序列,直接返回。这里有些人用map来作为标记,这样比我的节省了很多空间,不过我用字符数组所以就直接耶同样用字符数组存储。详细代码如下:
//模拟
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define INF 0x3f3f3f3f
# define LOCAL
using namespace std;
const int maxn = 105;
char s1[maxn],s2[maxn],tmp[maxn*2],s[maxn*2],bol[1005][maxn*2];//bol数组存储已经整合过的字符串
int len,ans;//ans存储答案
bool judge(){
for(int i=0;i<ans+1;i++)
if(!strcmp(bol[i],tmp))
return true;
return false;
}//判断是否已存储字符串
bool bfs(){
memset(bol,'0',sizeof(bol));
int l = 0;
for(int i=0;i<len;i++){
tmp[l++] = s2[i];
tmp[l++] = s1[i];
}
tmp[l] = '\0';//结束符!!!!
ans = 0;
while(!judge()){
strcpy(bol[ans],tmp);
ans++;
if(!strcmp(tmp,s))
return true;
for(int i=0;i<len;i++){
s1[i] = tmp[i];
s2[i] = tmp[len+i];
}
l = 0;
for(int i=0;i<len;i++){
tmp[l++] = s2[i];
tmp[l++] = s1[i];
}
tmp[l] = '\0';//结束符!!!!
}
return false;
}
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
int n;
cin >> n;
for(int i=1;i<=n;i++){
cin >> len;
cin >> s1 >> s2 >> s;
cout << i << " ";
if(bfs())
cout << ans << endl;
else
cout << "-1" << endl;
}
return 0;
}
Pots POJ - 3414
这道题是倒水问题,有倒水的规则。同样是用bfs解题。这里想要提一下的是,如何记录在查找过程中的各个操作,因为题目最后要求要输出操作,所以我们先用一个二维字符数组将所有操作存储起来,然后再bfs,进栈出栈的过程中记录所对应的下标,这里我用到的是在结构体里多定义一个记录操作的字符数组用来记录每一步的路径,其中下标表示是第几步,对应的字符则是以字符形式存储的操作的下标。在每一次压入栈的时候,我们都需要将上一步操作的字符数组复制下来,并将当前的操作下标存进去。
#include<cstdio>
#include<cstring>
#include<queue>
# define LOCAL
using namespace std;
const int maxn = 105;
const int N = 10;
int a,b,c;
bool vis[maxn][maxn];//判断是否已经出现这种情况
char str[N][N] = {"","FILL(1)","FILL(2)","DROP(1)","DROP(2)","POUR(1,2)","POUR(2,1)"};//输出的字符串
struct node{
int a,b;//第一瓶水和第二瓶水
int step;//路径长度
char print[1005];//记录路径
};//定义每一步的各个值
void bfs(){
node t={0,0,0,'0'},tmp;//初始化
queue<node> q;
q.push(t);
vis[t.a][t.b] = true;
while(!q.empty()){
node cur = q.front();
q.pop();
if(cur.a == c || cur.b == c){
printf("%d\n",cur.step);
for(int i=0;i<cur.step;i++)
printf("%s\n",str[cur.print[i]-'0']);
return;
}//结束条件
for(int i=1;i<=6;i++){
bool flag = false;
if(i==1 && cur.a < a){//把第一个瓶子装满
tmp.a = a;
tmp.b = cur.b;
flag = true;
}else if(i==2 && cur.b < b){ //把第二个瓶子装满
tmp.a = cur.a;
tmp.b = b;
flag = true;
}else if(i==3 && cur.a!=0){//把第一个瓶子倒了
tmp.a = 0;
tmp.b = cur.b;
flag = true;
}else if(i==4 && cur.b!=0){//把第二个瓶子倒了
tmp.a = cur.a;
tmp.b = 0;
flag = true;
}else if(i==5 && cur.a!=0 && cur.b<b){//把第一个倒进第二个
if(b-cur.b >= cur.a){
tmp.b = cur.a + cur.b;
tmp.a = 0;
}else{
tmp.a = cur.a+ cur.b - b;
tmp.b = b;
}
flag = true;
}else if(i==6 && cur.b!=0 && cur.a<a){//把第二个倒进第一个
if(a-cur.a >= cur.b){
tmp.a = cur.a + cur.b;
tmp.b = 0;
}else{
tmp.b = cur.a + cur.b - a;
tmp.a = a;
}
flag = true;
}
if(flag){
if(vis[tmp.a][tmp.b])//判断是否出现过,如果没有出现过,则压入栈
continue;
vis[tmp.a][tmp.b] = true;
tmp.step = cur.step + 1;//路径长度+1
strcpy(tmp.print,cur.print);//复制上一个记录的路径
tmp.print[cur.step] = i + '0';//标记当前路径
q.push(tmp);
}
}
}
printf("impossible\n");
return;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
scanf("%d%d%d",&a,&b,&c);
memset(vis,false,sizeof(vis));
bfs();
return 0;
}
非常可乐 HDU - 1495
这道题有两种解法,我一开始只想到BFS,但发现有位大佬直接用数论写20行代码把它做出来了,很震惊,也研究了很久。这里给下大佬的链接
超强!!
然后我也发现网上很多可能是抄这位大佬的,解析什么都没有,就直接把大佬的代码复制过来,当然也可能是我太菜了.这里我大概补充一些我原来没有理解到的地方,关于方程转化,可以通过约分进行简化,其实约分后得到的就是最大公因数.然后我们可以通过移位得到x的表达式然后再进行换元,把c消掉,这样就使得每个表达式只有一个变量,然后关于x和y,我们由通解的形式可以看出是一正一负,所以再去绝对值的时候需要注意下.
#include<cstdio>
int gcd(int n,int m){
return m?gcd(m,n%m):n;
}
int main(){
int s,n,m;
while(~scanf("%d%d%d",&s,&n,&m),s+n+m){
s /= gcd(n,m);
if(s&1)
printf("NO\n");
else
printf("%d\n",s-1);
}
return 0;
}
我的思路----bfs
这个其实和上一题很像,可以说只是换了一下形式,所以我第一反应就是用bfs,也没有想其他的.,关于思路,其实也差不多,所以就写了一些注释以供参考.
#include<cstdio>
#include<cstring>
#include<queue>
# define LOCAL
using namespace std;
const int maxn = 105;
int v[5];
bool vis[maxn][maxn][maxn];//判断是否已经出现这种情况
struct node{
int v[5];//可乐瓶、第一瓶水和第二瓶水
int step;//路径长度
}tmp;//定义每一步的各个值
void pour(int i,int j){
int sum = tmp.v[i] + tmp.v[j];
if(sum >= v[j])
tmp.v[j] = v[j];
else
tmp.v[j] = sum;
tmp.v[i] = sum - tmp.v[j];
}
void bfs(){
node t;//初始化
t.v[1] = v[1],t.v[2]=t.v[3]=t.step=0;
queue<node> q;
q.push(t);
vis[t.v[1]][0][0] = true;
while(!q.empty()){
node cur = q.front();
q.pop();
if(cur.v[1]==cur.v[3] && cur.v[2]==0){
printf("%d\n",cur.step);
return;
}
for(int i=1;i<4;i++)
for(int j=1;j<4;j++){
if(i!=j){
tmp = cur;
pour(i,j);
if(!vis[tmp.v[1]][tmp.v[2]][tmp.v[3]]){
tmp.step++;
q.push(tmp);
vis[tmp.v[1]][tmp.v[2]][tmp.v[3]] = true;
}
}
}
}
printf("NO\n");
return;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
while(~scanf("%d%d%d",&v[1],&v[2],&v[3]),v[1]+v[2]+v[3]){
if(v[2]>v[3]){
int t=v[2];v[2]=v[3];v[3]=t;
}
memset(vis,false,sizeof(vis));
bfs();
}
return 0;
}
Fire! UVA - 11624
这道题的话,刚开始我以为火源会移动,后面才直到只是向四周扩散.这里就用到了两次队列.我们可以先预处理火源达到每一位置的时间,通过队列实现.然后再看joe怎么走,如果走的位置是在火源路径的话,就需要判断先后,如果joe先则可以走.详细代码如下:
//两次队列
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
# define LOCAL
using namespace std;
const int maxn = 1005;
const int inf = 0x3f3f3f3f;
int n,r,c;
int d[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};//方向
char mp[maxn][maxn];//图
int firetime[maxn][maxn];//火到达某点的时间
bool vis[maxn][maxn];//判断是否出现过
struct node{
int x,y;
int t;
}joe;
bool judge(int x,int y){
if(x<0 || x>=r || y<0 || y>=c)
return false;
return true;
}//判断是否出界
queue<node> q;
void init(){
memset(vis,false,sizeof(vis));
memset(firetime,inf,sizeof(firetime));
while(!q.empty())
q.pop();
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(mp[i][j]=='J'){
joe.x = i,joe.y = j,joe.t = 0;//找到joe位置
}else if(mp[i][j]=='F'){
firetime[i][j] = 0;
vis[i][j] = true;
q.push((node){i,j,0});//找到火的位置并把它压入栈
}
}
}
while(!q.empty()){
node cur = q.front();
q.pop();
for(int i=0;i<4;i++){
int tx = cur.x + d[i][0];
int ty = cur.y + d[i][1];
if(judge(tx,ty) && !vis[tx][ty] && mp[tx][ty]=='.'){
vis[tx][ty] = true;
firetime[tx][ty] = cur.t + 1;
q.push((node){tx,ty,cur.t+1});
}
}
}//预处理火源蔓延位置的时间
}
void bfs(){
init();
memset(vis,false,sizeof(vis));
while(!q.empty())
q.pop();
q.push(joe);
vis[joe.x][joe.y] = true;
while(!q.empty()){
node cur = q.front();
q.pop();
for(int i=0;i<4;i++){
int tx = cur.x + d[i][0];
int ty = cur.y + d[i][1];
if(!judge(tx,ty)){
printf("%d\n",cur.t+1);
return;
}//判断已出界,则证明已逃脱
if(!vis[tx][ty] && mp[tx][ty]=='.' && cur.t+1 < firetime[tx][ty]){
vis[tx][ty] = true;
q.push((node){tx,ty,cur.t+1});
}//如果joe到达某个点的时间比火源小,则可以到达,压入栈
}
}
printf("IMPOSSIBLE\n");
return;//逃脱不了
}
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
cin >> n;
while(n--){
cin >> r >> c;
memset(mp,'#',sizeof(mp));//初始化mp
for(int i=0;i<r;i++)
cin >> mp[i];
bfs();
}
return 0;
}
Oil Deposits HDU - 1241
这道其实是紫书上一模一样的题目,所以看完题目就直接开始写代码了,所以也没有注释,这里就再说一下吧,这道题是求联通块的,就是我们在找到一个油田的时候需要找到和他相邻的油田,即同一个连通块.这里我们用dfs来做,我们用vis数组来标记是否是同一个联通块.我们先对图里面每一个位置进行筛选,就是找到没有标记的油田,然后调用dfs函数,dfs函数里再判断它周围的位置是否有油田,如果有的话就继续调用dfs函数,我们这里用cnt来表示联通块的个数,并作为标记.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
# define LOCAL
using namespace std;
const int maxn = 105;
char mp[maxn][maxn];
int vis[maxn][maxn];
int n,m;
void dfs(int i,int j,int cnt){
if(i<0 || i>=m || j<0 || j>=n) return;
if(mp[i][j]=='#' || vis[i][j]!=0) return;
vis[i][j] = cnt;
for(int k=-1;k<=1;k++)
for(int l=-1;l<=1;l++)
if(mp[i+k][j+l]=='@' && vis[i+k][j+l]==0)
dfs(i+k,j+l,cnt);
}
int main(){
std::ios::sync_with_stdio(false);
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d",&m,&n)==2 && m){
for(int i=0;i<m;i++)
scanf("%s",&mp[i]);
int cnt = 0;
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
for(int j=0;j<n;j++){
if(vis[i][j]==0 && mp[i][j]=='@')
dfs(i,j,++cnt);
}
printf("%d\n",cnt);
}
return 0;
}
Find a way HDU - 2612
这道题是找两个人的最短路径,这里其实我们可以借鉴fire的思路,即两次队列,先对第一个人到达每一个KFC的时间用 t数组记录下来,然后再通过队列得到第二个人到达每一个KFC的路径,这里我们用一个全局表量nmin来表示最小的路径,因为每段路径时间是一样的,即对于每一个KFC将第一个人和第二个人的路径相加,不断比较,最后得到最少的时间nmin*11.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
# define LOCAL
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 205;
int d[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//方向
char mp[maxn][maxn];//图
bool vis[maxn][maxn];//判断是否在前面已经出现过
int step[maxn][maxn];//走过的街道
int n,m;
int nmin;//走过的最小街道数
struct node{
int x,y;
int step;
}v[2];//定义两个人数据
bool judge(int i,int j){
if(i<0 || j<0 || i>=n || j>=m) return false;
if(vis[i][j] || mp[i][j]=='#') return false;
return true;
}//判断是否符合条件
queue<node> q;
void bfs(){
memset(vis,false,sizeof(vis));//初始化
while(!q.empty())
q.pop();//清空队列
q.push(v[0]);//把第一个人压入栈
vis[v[0].x][v[0].y] = true;//标记
while(!q.empty()){
node cur = q.front();
q.pop();
for(int i=0;i<4;i++){
node tmp=cur;
tmp.x = cur.x + d[i][0];
tmp.y = cur.y + d[i][1];//各个方向走
if(judge(tmp.x,tmp.y) && (mp[tmp.x][tmp.y]=='.'||mp[tmp.x][tmp.y]=='@')){
vis[tmp.x][tmp.y] = true;
tmp.step = cur.step + 1;//走过的街道数加一
if(mp[tmp.x][tmp.y]=='@')//判断是否到达KFC
step[tmp.x][tmp.y] = min(step[tmp.x][tmp.y],tmp.step);
q.push(tmp);//如果是街道继续走,压入栈
}
}
}
memset(vis,false,sizeof(vis));
while(!q.empty())
q.pop();
q.push(v[1]);
vis[v[1].x][v[1].y] = true;
while(!q.empty()){
node cur = q.front();
q.pop();
for(int i=0;i<4;i++){
node tmp=cur;
tmp.x = cur.x + d[i][0];
tmp.y = cur.y + d[i][1];
if(judge(tmp.x,tmp.y) && (mp[tmp.x][tmp.y]=='.'||mp[tmp.x][tmp.y]=='@')){
vis[tmp.x][tmp.y] = true;
tmp.step = cur.step + 1;//走过的街道数加一
if(mp[tmp.x][tmp.y]=='@')//判断是否到达KFC
nmin = min(step[tmp.x][tmp.y]+tmp.step,nmin);
q.push(tmp);//如果是街道继续走,压入栈
}
}
}
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d",&n,&m)==2){
memset(step,inf,sizeof(step));
nmin = inf;
for(int i=0;i<n;i++){
scanf("%s",mp[i]);
for(int j=0;j<m;j++)
if(mp[i][j]=='Y'){
v[0].x = i,v[0].y = j,v[0].step=0;
}else if(mp[i][j] == 'M'){
v[1].x = i,v[1].y = j,v[1].step=0;
}
}
bfs();
printf("%d\n",nmin*11);
}
return 0;
}
这里我还用了另一个方法,其实也差不多,只是把重复的代码合在一起,调用两次.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
# define LOCAL
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 205;
int d[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//方向
char mp[maxn][maxn];//图
bool vis[maxn][maxn];//判断是否在前面已经出现过
int dis[maxn][maxn][2];
int n,m;
struct node{
int x,y;
int step;
};
bool judge(int i,int j){
if(i<0 || i>=n || j<0 || j>=m) return false;
if(vis[i][j] || mp[i][j]=='#') return false;
return true;
}
void bfs(int x,int y,int flag){
node cur,tmp;
cur.x = x,cur.y = y,cur.step = 0;
queue<node> q;
q.push(cur);
vis[cur.x][cur.y] = true;
while(!q.empty()){
cur = q.front();
q.pop();
for(int i=0;i<4;i++){
tmp.x = cur.x + d[i][0];
tmp.y = cur.y + d[i][1];
if(judge(tmp.x,tmp.y) && (mp[tmp.x][tmp.y]=='.'||mp[tmp.x][tmp.y]=='@')){
vis[tmp.x][tmp.y] = true;
tmp.step = cur.step + 1;
if(mp[tmp.x][tmp.y]=='@')
dis[tmp.x][tmp.y][flag] = min(dis[tmp.x][tmp.y][flag],tmp.step);
q.push(tmp);
}
}
}
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d",&n,&m)==2){
memset(dis,inf,sizeof(dis));
for(int i=0;i<n;i++)
scanf("%s",mp[i]);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
if(mp[i][j]=='Y'){
memset(vis,false,sizeof(vis));
bfs(i,j,0);
}else if(mp[i][j] == 'M'){
memset(vis,false,sizeof(vis));
bfs(i,j,1);
}
}
int ans = inf;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(mp[i][j]=='@' && ans>dis[i][j][0]+dis[i][j][1])
ans = dis[i][j][0] + dis[i][j][1];
printf("%d\n",ans*11);
}
return 0;
}
题外话
今天把[kuanbin带我飞]的专题1做完了,有一道Vjudge交不了,感觉收获很多,继续加油!