@wwwqeqeqeqe
2019-05-10T15:20:39.000000Z
字数 16978
阅读 899
搜索
A Cleaning Robot (POJ 2688)
题目大意:
题目给出一张地图,地图中有四种符号,其中,'o'表示机器人的初始位置,'.'表示可以通过的地板,'x'表示家具的位置,相当于墙,'*'表示脏地板,需要我们去清扫。题目要求机器人清扫完所有的脏地板并且所走距离最小,如果存在,则输出这个最小距离,如果不存在,则输出-1.
解题思路:
我们将机器人和每一块脏地板看成一个图上的各个顶点,那么题目就变为了在一个无向图中,遍历各个顶点至少一次,使总路程最小的问题,这样就变为了一个典型的TSP问题,我们再通过状压DP来进行解决。
AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int dx[]= {1,0,-1,0};
const int dy[]= {0,1,0,-1};
int r,c,psum,ans;
char s[25][25];
int mp[12][12];
bool f[12];
struct node
{
int x,y,l;
};
node P[12];
int fnd(int x,int y)
{
for(int i=0; i<psum; ++i)
{
if(P[i].x==x && P[i].y==y)
return i;
}
}
int init()
{
cin >> c >> r;
if(r==0 && c==0)
return 0;
int i,j;
psum=1;
for(int i=0; i<r; ++i)
{
cin >> s[i];
for(j=0; j<c; ++j)
{
if(s[i][j]=='o')
{
P[0].x=i;
P[0].y=j;
s[i][j]='*';
}
else if(s[i][j]=='*')
{
P[psum].x=i;
P[psum++].y=j;
}
}
}
queue<node> q;
node u,v;
for(i=0; i<12; ++i)
for(j=0; j<12; ++j)
mp[i][j]=INF;
int xx,yy;
for(i=0; i<psum; ++i)
{
int f[25][25];
memset(f,0,sizeof(f));
u.x=P[i].x;
u.y=P[i].y;
u.l=0;
q.push(u);
f[u.x][u.y]=1;
int cnt=psum;
while(!q.empty()&&cnt)
{
u=q.front();
q.pop();
for(j=0; j<4&&cnt; ++j)
{
xx=u.x+dx[j];
yy=u.y+dy[j];
if(xx<0 || xx>=r || yy<0 || yy>=c)
continue;
if(s[xx][yy]=='x' || f[xx][yy])
continue;
if(s[xx][yy]=='*')
{
cnt--;
mp[i][fnd(xx,yy)]=u.l+1;
}
f[xx][yy]=1;
v.x=xx;
v.y=yy;
v.l=u.l+1;
q.push(v);
}
}
while(!q.empty())
q.pop();
}
return 1;
}
bool is()
{
int i;
for(i=1; i<psum; ++i)
if(mp[0][i]==INF)
return 1;
return 0;
}
void dfs(int t,int sum,int cur)
{
if(cur==psum)
{
if(sum<ans) ans=sum;
return;
}
for(int i=1; i<psum; ++i)
{
if(!f[i] && sum+mp[t][i]<ans)
{
f[i]=1;
dfs(i,sum+mp[t][i],cur+1);
f[i]=0;
}
}
}
int main()
{
while(init())
{
if(is())
{
cout << -1 << endl;
continue;
}
ans=INF;
memset(f,0,sizeof(f));
dfs(0,0,1);
if(ans==INF || ans==0)
ans=-1;
cout << ans << endl;
}
return 0;
}
B Bloxorz I (POJ 3322)
题目大意:
题目给出一张地图,地图中有一个1*2的方块,题目要求求出最少的滚动次数,将这个1*2的方块从地图中的某个空格处滑落。地图中一共有五种字符,其中,'#'表示墙,是不能通过的,'X'表示初始方块的位置,可能有1到2个,表示方块一开始是横放或竖放在这1到2个方格上的。'E'表示这个方块是脆弱的方块,只能承受横放方块的重量,即方块不能竖立在这种方块上,'.'表示可以正常通过的方块,'O'表示最后我们要滑落的终点。题目保证'x'和'O'的位置的方块都能承受单个方块的重量(即不是脆弱的方块),而且最后到达终点时,方块一定是竖着的(因为最终终点只有一个O)。
解题思路:
我们可以将方块的方向分别用数字表示,0表示竖着的方块,1表示竖着放置的方块,2表示横着放置的方块。记录完方块姿态后,将横放着的方块标记为左边那个点,竖放着的点记录为上面那个点。接着,就可以通过BFS对方块进行搜索,注意搜索过程中对‘E’和‘#’的判断,最后得到状态为0的时候标记点在终点的最小答案。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 5;
const int MAXM = 1e3 + 5;
const int dx[3][4] = {{-2,1,0,0},{-1,2,0,0},{0,0,-1,1}};
const int dy[3][4] = {{0,0,-2,1},{0,0,-1,1},{-1,2,0,0}};
const int dz[3][4] = {{1,1,2,2},{0,0,1,1},{0,0,2,2}};
char g[MAXN][MAXM];
int n,m,sx,sy,sz,ex,ey;
struct Point
{
int x,y,z,step;
Point() {}
Point(int _x, int _y, int _z, int _step)
{
x = _x;
y = _y;
z = _z;
step = _step;
}
};
queue<Point> q;
int b[MAXN][MAXM][4];
int check(Point a)
{
if(a.z == 0 && !(a.x > n || a.x < 1 || a.y > m || a.y < 1) && !b[a.x][a.y][a.z] && g[a.x][a.y] != 'E' && g[a.x][a.y] != '#')
return 0;
if(a.z == 1 && !(a.x < 1 || a.x + 1 > n || a.y < 1 || a.y > m) && !b[a.x][a.y][a.z] && g[a.x][a.y] != '#' && g[a.x + 1][a.y] != '#')
return 0;
if(a.z == 2 && !(a.x < 1 || a.x > n || a.y < 1 || a.y + 1 > m) && !b[a.x][a.y][a.z] && g[a.x][a.y] != '#' && g[a.x][a.y + 1] != '#')
return 0;
return 1;
}
int bfs()
{
memset(b, 0, sizeof(b));
while(q.size())
q.pop();
q.push(Point(sx, sy, sz, 0));
Point now;
b[sx][sy][sz] = true;
while(q.size())
{
now = q.front();
q.pop();
for(int i = 0; i < 4; ++ i)
{
Point tmp;
tmp = Point(now.x + dx[now.z][i],now.y + dy[now.z][i], dz[now.z][i], now.step + 1);
if(check(tmp))
continue;
if(tmp.x == ex && tmp.y == ey && !tmp.z)
return tmp.step;
q.push(tmp);
b[tmp.x][tmp.y][tmp.z] = 1;
}
}
return -1;
}
int main()
{
while (~scanf("%d %d", &n, &m) && n)
{
for(int i = 1; i <= n; ++ i)
scanf("%s", g[i] + 1);
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j <= m; ++ j)
{
if (g[i][j] == 'O')
ex = i,ey = j;
else if (g[i][j] == 'X' && g[i + 1][j] == 'X')
{
sx = i,sy = j;
g[i + 1][j] = '.';
sz = 1;
}
else if(g[i][j] == 'X' && g[i][j + 1] == 'X')
{
g[i][j + 1] = '.';
sx = i,sy = j;
sz = 2;
}
else if(g[i][j] == 'X')
{
sx = i,sy = j ;
sz = 0;
}
}
}
int ans = bfs();
if(ans == -1)
printf("Impossible\n");
else
printf("%d\n", ans);
}
return 0;
}
C Remmarguts' Date (POJ 2449)
题目大意:
题目给出一张有向图,图中有n个点m条边,接下来给出m条边的起始端点,结束端点以及他们之间的距离。最后给出地图的起点和终点,问从起点到终点的第K大的距离时多少。没有就输出-1.
解题思路:
首先,我们可以通过dijkstra或者spfa处理出所有点到终点的距离,保存在一个数组中。然后采用A*算法,按照g+h从小到大的顺序进行排序,其中,g表示从起点到当前点的距离,h表示当前点到终点的预期距离,即之前通过最短路求到的那个值。然后用times记录每个点被找到的次数,直到记录到第k次找到终点即可。因为我们要求的是所有点到终点的距离,而且这个图是有向图,所以我们还需要建立一个反图来跑dijkstra。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3+5;
struct vertex
{
int sum, h, pos;
bool operator < (vertex a) const
{
return a.sum + a.h < sum + h;
}
};
struct sc
{
int u, v, w, next;
};
sc line1[MAXN*MAXN],line2[MAXN*MAXN];
int link1[MAXN],link2[MAXN],h[MAXN],times[MAXN];
int n, m, s, e, k;
bool vis[MAXN];
priority_queue <vertex> que;
void init()
{
memset(link1, 0, sizeof(link1));
memset(link2, 0, sizeof(link2));
memset(vis,0,sizeof(vis));
memset(h,0x3f,sizeof(h));
while (!que.empty())
que.pop();
memset(times,0,sizeof(times));
}
void djikstra()
{
int i,k,p;
h[e] = 0;
for (p = 1; p <= n; ++p)
{
k = 0;
for (i = 1; i <= n; ++i)
if (!vis[i] && (!k||h[i]<h[k])) k = i;
vis[k] = true;
k = link2[k];
while (k)
{
if (h[line2[k].v] > h[line2[k].u] + line2[k].w)
h[line2[k].v] = h[line2[k].u] + line2[k].w;
k = line2[k].next;
}
}
}
int Astar()
{
int t;
vertex g,temp;
g.pos = s;
g.sum = 0;
g.h = h[s];
que.push(g);
if (s==e) ++k;
while (!que.empty())
{
g = que.top();
que.pop();
++times[g.pos];
if (times[g.pos] == k && g.pos == e)
return g.sum + g.h;
if (times[g.pos] > k)
continue;
t = link1[g.pos];
while (t)
{
temp.sum = g.sum + line1[t].w;
temp.h = h[line1[t].v];
temp.pos = line1[t].v;
que.push(temp);
t = line1[t].next;
}
}
return -1;
}
int main()
{
while(cin >> n >> m)
{
init();
for (int i = 1; i <= m; ++i)
{
cin >> line1[i].u >> line1[i].v >> line1[i].w;
line1[i].next = link1[line1[i].u];
link1[line1[i].u] = i;
line2[i].u = line1[i].v;
line2[i].v = line1[i].u;
line2[i].w = line1[i].w;
line2[i].next = link2[line2[i].u];
link2[line2[i].u] = i;
}
cin >> s >> e >> k;
djikstra();
cout << Astar() << endl;
}
return 0;
}
D Weather Forecast (POJ 2044)
题目大意:
题目给出一个由4*4的方格组成的图形,表示一个国家和这个国家的16个地区。我们作为风神,需要使一个2*2的云在这个国家中运动,每次这个云可以向东西南北四个方向中的一个运动1或2格,或者这个云不动。输入首先输入一个n表示我们需要探测的n天,接下来的n行,每行16个数表示这16个地区,其中0表示平时,1表示赶集日或节日,其中这一天这个地区不能下雨(及有云)。并且我们还必须保证每个地区不会出现连续7天没有下雨的情况。
解题思路:
我们要保证16个方格中,每个方格7天内都下过雨,因为云是2*2的,那么,我们只需要保证7天内四个角都下过去即可。对于云,我们每次都九种不同的运动规则,暴力循环搜索即可。在搜索的过程中,我们开一个6维的数组,分别表示第i天,云处于第x个位置,四个角落有多少天没有被下雨,在后面查找的过程中,如果发现这个状态已经找过,就不用再找了。对于某个城市某一天的情况,可以使用位运算来进行表示。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int DAY[367];
bool mark[366][10][8][8][8][8];
int ys[12] = {0,1,2,3,0,4,5,6,0,7,8,9};
int dir[9][2] = {0,0,0,1,0,-1,1,0,-1,0,0,2,0,-2,2,0,-2,0};
int OK,T;
bool ok(int t,int x,int y,int a[])
{
int aa = (x - 1) * 4 + y;
int bb= (x - 1 + 1) * 4 + y;
int cc = (x - 1) * 4 + y + 1;
int dd = (x - 1 + 1) * 4 + y + 1;
if(x < 1 || x > 3 || y < 1 || y > 3)
return 0;
if(a[1] >= 7 || a[2] >= 7 || a[3] >= 7 || a[4] >= 7)
return 0;
if(((1 << aa) & DAY[t]) || ((1 << bb) & DAY[t]) || ((1 << cc) & DAY[t]) || ((1 << dd) & DAY[t]))
return 0;
if(mark[t][ys[aa]][a[1]][a[2]][a[3]][a[4]])
return 0;
return 1;
}
void DFS(int t,int x,int y,int a[])
{
if(t == T)
OK = 1;
if(OK)
return ;
int b[5];
for(int i = 0 ; i < 9 ; i ++)
{
if(t == 0 && i)
break;
int nowt = t + 1;
int nowx = x + dir[i][0];
int nowy = y + dir[i][1];
for(int j = 1 ; j <= 4 ; j ++)
b[j] = a[j] + 1;
if(nowx == 1 && nowy == 1) b[1] = 0;
if(nowx == 1 && nowy == 3) b[2] = 0;
if(nowx == 3 && nowy == 1) b[3] = 0;
if(nowx == 3 && nowy == 3) b[4] = 0;
if(ok(nowt,nowx,nowy,b))
{
mark[nowt][ys[(nowx-1)*4+nowy]][b[1]][b[2]][b[3]][b[4]] = 1;
DFS(nowt,nowx,nowy,b);
}
}
return ;
}
int main ()
{
int i,j;
while(cin >> T && T)
{
memset(DAY,0,sizeof(DAY));
for(i = 1 ; i <= T ; i ++)
{
int tmp;
for(j = 1 ; j <= 16 ; j ++)
{
cin >> tmp;
DAY[i] = DAY[i] | (tmp << j);
}
}
memset(mark,0,sizeof(mark));
OK = 0;
int a[5];
a[1] = a[2] = a[3] = a[4] = 0;
DFS(0,2,2,a);
cout << OK << endl;
}
return 0;
}
E Holedox Moving (POJ 1324)
题目大意:
题目首先给出一张地图,地图中有一条蛇,蛇的长度为L,接下来L行,每行表示蛇身从头到尾的坐标。接下来有K行,每行也有一个坐标,表示地图中墙的位置。最后问蛇能否从当前位置移动到(1,1)这个位置(即蛇头到达(1,1)),蛇不能通过墙和自己的身体。
解题思路:
因为蛇身长度最大为8并且是连续的,那么,我们可以通过一个状态压缩,来表示每两个蛇身之间的相对应位置。每个相对位置占用两位,最多就7对,即14个相对位置,即可把所有情况表示完。所以,我们可以开一个三维数组,前两位表示蛇头的位置,第三位表示后面蛇身相对于他的前一个位置的方向,每两位表示一个方向。即vis[21][21][1<<14]即可表示出所有情况。接下来,我们就可以通过A*或BFS对蛇头进行搜索,得到答案。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int n,m,l,k,mp[25][25];
int dir[3][3]= {-1,1,-1,3,-1,2,-1,0,-1};
int D[4][2]= {1,0,-1,0,0,1,0,-1};
bool vis[21][21][1<<14];
struct Body
{
int x[10],y[10];
int cnt;
} body,bb;
bool check(int a,int b,Body c)
{
for(int i=2; i<=l; i++)
{
if(a==c.x[i]&&b==c.y[i])
return true;
}
return false;
}
int bfs()
{
queue<Body>q;
body.cnt=0;
q.push(body);
while(!q.empty())
{
body=q.front();
q.pop();
if(body.x[1]==1&&body.y[1]==1)
return body.cnt;
for(int i=0; i<4; i++)
{
int x=body.x[1]+D[i][0],y=body.y[1]+D[i][1];
if(x<1||x>n||y<1||y>m||mp[x][y])
continue;
if(check(x,y,body))
continue;
body.x[0]=x;
body.y[0]=y;
int sta=0,tmp1,tmp2;
for(int j=l; j>=1; j--)
{
bb.x[j]=body.x[j-1];
bb.y[j]=body.y[j-1];
if(j!=l)
{
tmp1=bb.x[j]-bb.x[j+1]+1;
tmp2=bb.y[j]-bb.y[j+1]+1;
int tmp=dir[tmp1][tmp2];
tmp=(tmp<<(14-j*2));
if(dir[tmp1][tmp2]!=-1) sta=(sta|tmp);
}
}
if(vis[x][y][sta])
continue;
vis[x][y][sta]=1;
bb.cnt=body.cnt+1;
q.push(bb);
}
}
return -1;
}
int main()
{
int cas=0,x,y;
while(cin >> n >> m >> l&&n&&m&&l)
{
memset(mp,0,sizeof(mp));
memset(vis,0,sizeof(vis));
int sta=0;
scanf("%d%d",&body.x[1],&body.y[1]);
for(int i=2; i<=l; i++)
{
cin >> body.x[i] >> body.y[i];
x=body.x[i-1]-body.x[i]+1;
y=body.y[i-1]-body.y[i]+1;
int tmp=dir[x][y];
tmp=(tmp<<(14-(i-1)*2));
if(dir[x][y]!=-1)
sta=(sta|tmp);
}
vis[body.x[1]][body.y[1]][sta]=1;
cin >> k;
for(int i=0; i<k; i++)
{
cin >> x >> y;
mp[x][y]=1;
}
cout << "Case " << ++cas << ": " << bfs() << endl;
}
return 0;
}
F Quantum (POJ 2908)
题目大意:
题目第一行给出一个数字N,表示接下来有N组测试数据。对于每组测试数据,输入三个数L,nop,nw,表示要处理的二进制串的长度,操作种类数以及当前需要操作的二进制串的数量。最后,要求出对于给出的所有二进制串的花费的最小值。
解题思路:
我们看到这样的题,因为L是≤20的,那么,我们就可以将每个串转化为十进制进行存储。因为我们要得到的是消耗最小,我们可以通过BFS来枚举我们得到的每一种变换后的情况是怎么样的,再通过优先队列,并手写一个结构体来实现优先队列的排序,每次BFS之后把消耗最小的优先出队列。因为对于每个点,我们有多种到达的方法,我们在BFS的同时,记录到达每个点所需要的最小消耗,并及时更新。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1<<22;
const int INF=0x3f3f3f3f;
int vis[N+5];
char c[35][35];
char c1[35],c2[35],c3[35];
int a1[35],a2[35];
int a[35];
int l,n,m,ans;
struct node
{
char s[35];
int h;
bool operator<(const node &k) const
{
return h>k.h;
}
};
int vs(char k[])
{
int s=0;
int i=0;
while(k[i++])
s=s*2+k[i-1]-'0';
return s;
}
void vc(char s1[],char s2[])
{
for(int i=0;i<l;i++)
{
if(s2[i]=='N')
c3[i]=s1[i];
if(s2[i]=='F')
{
if(s1[i]=='0')
c3[i]='1';
if(s1[i]=='1')
c3[i]='0';
}
if(s2[i]=='S')
c3[i]='1';
if(s2[i]=='C')
c3[i]='0';
}
}
void bfs()
{
memset(vis,0,sizeof(vis));
priority_queue<node> que;
node st;
strcpy(st.s,c1);
st.h=0;
que.push(st);
ans=INF;
node k1;
while(!que.empty())
{
node k=que.top();
que.pop();
if(vis[vs(k.s)])
continue;
if(strcmp(k.s,c2)==0)
{
ans=k.h;
break;
}
vis[vs(k.s)]=1;
for(int i=1;i<=n;i++)
{
vc(k.s,c[i]);
if(!vis[vs(c3)])
{
strcpy(k1.s,c3);
k1.h=k.h+a[i];
que.push(k1);
}
}
}
}
int main()
{
int T;
cin >> T;
while(T--)
{
cin >> l >> n >> m;
for(int i=1;i<=n;i++)
cin >> c[i] >> a[i];
for(int i=1;i<=m;i++)
{
memset(c3,0,sizeof(c3));
cin >> c1 >> c2;
bfs();
if(i!=1)
cout << " ";
if(ans==INF)
cout << "NP";
else
cout << ans;
}
cout << endl;
}
}
G Full Tank? (POJ 3635)
题目大意:
给出一张图,共有n个小镇和m条公路,小镇编号为0~n-1。然后给出每个小镇加每升油需要多少钱。接着一共m行,每行3个数,表示从小镇u到小镇v需要d升汽油。然后输入一个数q表示查询次数,每次查询输入三个数,分别表示汽车的油箱容量,起始小镇和最终要到达的小镇。问完成这些旅行至少需要花费多少钱。(一开始油箱为空)。若无法到达,则输出“impossible”。
解题思路:
我们可以设置一个数组dp[i][j],表示到达点i时,还剩j升油的最小花费,那么,显然,dp[i][j]=min(dp[i][j],(edge(i,i')+(j-k))*price[i']+dp[i'][k]).然后每次都把得到的dp[i][j]的值放入一个优先队列中,每次从中间取出最小的那个进行计算更新。然后,就TLE了。为什么呢?因为我们这里每次计算的是加(edge(i,i')+(j-k))这么多升油,而在加这么多升油的过程中,我们可能会把许多比较劣的情况加到我们的优先队列里,使得后面我们计算了许多没必要的东西。那么怎么改呢?我们只需要改为每个点只枚举不加油直接走和只加一升油的情况就可以了,这样同样可以找遍所有的走法,但因为我们每次加的油比较少,就可以避免很多加X升油浪费掉这种比较劣的情况,极大的减少了每次的分支数量。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int MAXN=1005;
const int MAXM=100005;
const int INF=0x3f3f3f3f;
struct Edge
{
int v, w, next;
}edge[MAXM];
struct Node
{
int v, cost, f;
bool operator <(const Node &a) const{
return a.cost < cost;
}
};
int head[MAXN], e, n, m, cap;
int dp[MAXN][105], used[MAXN][105], p[MAXN];
int s, t, T;
priority_queue<Node>q;
void init()
{
memset(head, -1, sizeof(head));
e = 0;
}
void ready()
{
for(int i = 0; i < n; i++)
for(int j = 0; j <= 100; j++)
dp[i][j] = INF;
dp[s][0] = 0;
memset(used, 0, sizeof(used));
while(!q.empty())
q.pop();
}
void insert(int x, int y, int w)
{
edge[e].v = y;
edge[e].w = w;
edge[e].next = head[x];
head[x] = e++;
}
int bfs()
{
Node a, b;
a.v = s, a.cost = 0, a.f = 0;
q.push(a);
while(!q.empty())
{
a = q.top();
q.pop();
int u = a.v;
int cost = a.cost;
int f = a.f;
used[u][f] = 1;
if(u == t)
return cost;
if(f + 1 <= cap && !used[u][f + 1] && dp[u][f + 1] > dp[u][f] + p[u])
{
dp[u][f + 1] = dp[u][f] + p[u];
b.v = u;
b.f = f + 1;
b.cost = dp[u][f + 1];
q.push(b);
}
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
int w = edge[i].w;
if(f >= w && !used[v][f - w] && dp[v][f - w] > cost)
{
dp[v][f - w] = cost;
b.v = v;
b.f = f - w;
b.cost = dp[v][f - w];
q.push(b);
}
}
}
return -1;
}
int main()
{
int x, y, w;
cin >> n >> m;
init();
for(int i = 0; i < n; i++)
cin >> p[i];
while(m--)
{
cin >> x >> y >> w;
insert(x, y, w);
insert(y, x, w);
}
cin >> T;
while(T--)
{
cin >> cap >> s >> t;
ready();
int ans = bfs();
if(ans != -1)
cout << ans << endl;
else
cout << "impossible" <<endl;
}
return 0;
}
H Remainder (POJ 2426)
题目大意:
题目给出三个数n,k,m,每次对n进行一个操作,可以‘+’,‘-’,‘*’,‘%’m,然后使得到的数成为一个新的n。问最少需要多少次操作,才能使(最初始的n+1)%k=(当前n)%k。如果不存在,输出0,如果存在,输出最小次数并输出他们计算的符号顺序。如果存在多种顺序,按照'+'<'-'<'*'<'%'的顺序操作。
解题思路:
很显然,我们可以通过最初始的n,把这四种运算看做是四条边,每次BFS搜索这四种运算符即可。但是,我们发现,在我们进行运算的过程中,对同一个数取多次模运算是不会改变计算结果的,即,我们不管对m取多少次模,我们的答案是肯定不会变的,所以,'%'这个符号只会在我们的运算中出现一次。而又因为我们不管'+'或'-'多少个m,对于n%m的答案也是不会变的。(如果改变n的正负号是可以改变的,但m是正数,当n为正数时,最初始的n%k肯定为正,而负数模正数为负,肯定不会是我们需要的答案之一。)所以取模只会有两种情况,一开始就对n取模和n*m%m=0这两种。所以,对于我们的搜索就可以剪枝,减少大量搜索。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int maxn = 1e3+5;
int n,k,m,big;
char op_table[] = {'+','-','*','%'};
char op[maxn];
struct point
{
int num;
int father;
char op_pre;
} p[maxn];
bool exist[maxn];
void bfs()
{
int father=0,child=1;
int dest = (n+1+big)%k;
p[0].num = (n+big)%k;
p[0].op_pre = 0;
bool finish_flag = false,repeat_flag = false;
memset(exist,0,sizeof(exist));
memset(op,0,sizeof(op));
exist[p[0].num] = true;
int tmp;
while(father<child && !finish_flag && !repeat_flag)
{
bool validflag=false;
for(int i=0; i<4; i++)
{
switch(op_table[i])
{
case '+':
tmp = (p[father].num+m + big)%k;
break;
case '-':
tmp = (p[father].num-m + big)%k;
break;
case '*':
tmp = (p[father].num*m + big)%k;
break;
case '%':
if(father == 0 || (p[father].father == 0 && p[father].op_pre == '*') )
{
tmp = (father == 0) ? ((n%m+m)%m + big)%k : 0;
//father==0的情况,输入值不能是p[0].num,必须是n,因为前者已经对k取模了,而%不符合交换率;另一种情况,就直接赋值为0就可以了
break;
}
else
{
validflag = true;
}
default:
break;
}
if(validflag)//不符合两个条件的%操作符,就不需要考虑了
continue;
if(!exist[tmp])
{
exist[tmp] = true;
p[child].num = tmp;
p[child].father = father;
p[child].op_pre = op_table[i];
++child;
}
if(tmp==dest)
{
//output?
finish_flag = true;
break;
}
}
father++;
}
if(father == child)
{
cout << 0 << endl;
}
else if(finish_flag)
{
int x = child-1,count=0;
while(p[x].op_pre!=0)
{
op[count++] = p[x].op_pre;
x = p[x].father;
}
cout << count << endl;
for(int i=count-1; i>=0; --i)
{
cout << op[i];
}
cout << endl;
}
}
int main()
{
while(cin >> n >> k >> m)
{
if(n==0 && k==0 && m==0)
{
break;
}
big = k<<20;
bfs();
}
return 0;
}
I The Buses (POJ 1167)
题目大意:
题目给出某一车站12点到12点59分时所有公交车停靠这个公交车站的时间表,题目保证每一条公交线路的公交车到达车站的时间间隔是相同的,且最少每条公交线路有2辆车经过,最多有17条公交线路。问最少有可能有多少条公交线路。
解题思路:
我们可以根据题目预处理出所有的公交车线路,在通过DFS,看选择哪几条线路能恰好是数据中所有的数据刚好使用一次切选择的线路最少。由于每一条公交线路最少有2辆车经过,那么,只有前0~29分钟到车站的车才有可能是始发车。我们对所有的公交车线路按照停靠次数从大到小的顺序进行排序,当 当前已选的线路 加上 剩余没有安排的车辆除以当前路线需要的车 (即估计需要的线路数)比已经得到的答案大的,直接剪枝。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int ct[60];//ct[i]表示第i分钟到达的车数
int n,ans,tp;//车数 答案 总备选线路数
struct node
{
int s;//第一次到达时间
int j;//发车间隔
int t;//需要车数
} p[301];
int cmp(node a,node b)
{
return a.t>b.t;
}
bool test(int s,int ti) //s_起始时间 ti_间隔时间
{
for(int i=s; i<60; i+=ti)
if(!ct[i])
return false;
return true;
}
void dfs(int t,int now)
{
int i,tmp;
if(n==0)
{
if(now<ans)
ans=now;
return;
}
for(i=t; i<=tp && p[i].t>n; i++); //寻找合适线路,排除需要车数比剩余车数大的线路
for(int k=i; k<=tp; k++)
{
if(now+n/p[k].t>=ans)
return;//剪枝
if(test(p[k].s,p[k].j))
{
tmp=p[k].j;
for(int j=p[k].s; j<60; j+=tmp)
{
ct[j]--;
n--;
}
dfs(k,now+1);
for(int j=p[k].s; j<60; j+=tmp)
{
ct[j]++;
n++;
}
}
}
}
int main()
{
scanf("%d",&n);
int a;
for(int i=1; i<=n; i++)
{
scanf("%d",&a);
ct[a]++;
}
tp=0;
for(int i=0; i<=29; i++)
{
if(!ct[i])
continue;
for(int j=i+1; j<=59-i; j++)
{
if(test(i,j))
{
tp++;
p[tp].s=i;
p[tp].j=j;
p[tp].t=1+(59-i)/j;
}
}
}
sort(p+1,p+tp+1,cmp);
ans=17;
dfs(1,0);
printf("%d",ans);
return 0;
}
J Sudoku (POJ 2676)
题目大意:
题目给出多组数据,每组数据给出一个9*9的数独格子,题目要你将这个数独全部填完。(即把0全部填完)
解题思路:
我们可以开三个二维数组,row[i][x]表示在第i行数字x是否出现,col[i][x]表示在第i列数字x是否出现,grid[i][x]表示 在第i个3*3的格子内数字x是否出现。对于如何判断哪个位置在第i个3*3的格子内,可以通过图形式子进行推断。最后,直接暴力DFS即可。
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
int mp[10][10];
bool row[10][10]; //row[i][x] 标记在第i行中数字x是否出现了
bool col[10][10]; //col[j][y] 标记在第j列中数字y是否出现了
bool grid[10][10]; //grid[k][x] 标记在第k个3*3子格中数字z是否出现了
bool DFS(int x,int y)
{
if(x==10)
return true;
bool flag=false;
if(mp[x][y])
{
if(y==9)
flag=DFS(x+1,1);
else
flag=DFS(x,y+1);
if(flag) //回溯
return true;
else
return false;
}
else
{
int k=3*((x-1)/3)+(y-1)/3+1;
for(int i=1; i<=9; i++) //枚举数字1~9填空
if(!row[x][i] && !col[y][i] && !grid[k][i])
{
mp[x][y]=i;
row[x][i]=true;
col[y][i]=true;
grid[k][i]=true;
if(y==9)
flag=DFS(x+1,1);
else
flag=DFS(x,y+1);
if(!flag) //回溯,继续枚举
{
mp[x][y]=0;
row[x][i]=false;
col[y][i]=false;
grid[k][i]=false;
}
else
return true;
}
}
return false;
}
int main(int i,int j)
{
int test;
cin>>test;
while(test--)
{
memset(row,false,sizeof(row));
memset(col,false,sizeof(col));
memset(grid,false,sizeof(grid));
char MAP[10][10];
for(i=1; i<=9; i++)
for(j=1; j<=9; j++)
{
cin>>MAP[i][j];
mp[i][j]=MAP[i][j]-'0';
if(mp[i][j])
{
int k=3*((i-1)/3)+(j-1)/3+1;
row[i][ mp[i][j] ]=true;
col[j][ mp[i][j] ]=true;
grid[k][ mp[i][j] ]=true;
}
}
DFS(1,1);
for(i=1; i<=9; i++)
{
for(j=1; j<=9; j++)
cout<<mp[i][j];
cout<<endl;
}
}
return 0;
}