由于题目太多了,所以在更新过程中可能会出错,欢迎大家一起交流^_^。。。
第一题 题目:uva 11624 Fire
二维迷宫中,有一些着火点(可能不止一个),给出你的初始位置,人与着火点都是每秒钟向相邻的格子移动一格,问人能不能够在火势到达之前走到边界,如果能够,输出最短的时间
分析:对于着火点,可以预处理把所有的着火点都放进队列中,然后再用BFS处理出所有可燃烧的格子的最短到达时间。再用BFS来计算出人能够达到边界的最短间。。。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int X = 1005;
int dirx[] = {-1,1,0,0};
int diry[] = {0,0,-1,1};
int n;
int m;
bool use[X][X];
int tt[X][X];
char map[X][X];
struct node{
int x,y,step;
node(int _x,int _y,int _step){
x = _x;
y = _y;
step = _step;
}
};
struct point{
int x,y,step;
point(){}
point(int _x,int _y,int _step){
x = _x;
y = _y;
step = _step;
}
}q[X*X];
bool out(int x,int y){
return x>=n||y>=m||x<0||y<0;
}
int bfs(int sx,int sy){
memset(use,false,sizeof(use));
int head = 0;
int tail = 0;
q[tail++] = point(sx,sy,0);
while(head<tail){
point pre = q[head++];
int x = pre.x;
int y = pre.y;
if(x==0||y==0||x==n-1||y==m-1)
return pre.step;
pre.step ++;
for(int i=0;i<4;i++){
int dx = dirx[i]+x;
int dy = diry[i]+y;
if(out(dx,dy))
continue;
if(use[dx][dy]||tt[dx][dy]<=pre.step||map[dx][dy]=='#')
continue;
use[dx][dy] = true;
q[tail++] = point(dx,dy,pre.step);
}
}
return -1;
}
void init(){
memset(use,false,sizeof(use));
int head = 0;
int tail = 0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(map[i][j]=='F')
q[tail++] = point(i,j,0);
while(head<tail){
point pre = q[head++];
int x = pre.x;
int y = pre.y;
tt[x][y] = min(tt[x][y],pre.step ++);
for(int i=0;i<4;i++){
int dx = dirx[i]+x;
int dy = diry[i]+y;
if(out(dx,dy))
continue;
if(use[dx][dy]||map[dx][dy]=='#')
continue;
use[dx][dy] = true;
q[tail++] = point(dx,dy,pre.step);
}
}
}
void solve(){
cin>>n;
cin>>m;
for(int i=0;i<n;i++)
scanf("%s",map[i]);
memset(tt,0x3f,sizeof(tt));
int sx,sy;
sx = sy = 0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(map[i][j]=='J'){
sx = i;
sy = j;
}
init();
int ok = bfs(sx,sy);
if(ok==-1)
puts("IMPOSSIBLE");
else
printf("%d\n",ok+1);
}
int main(){
freopen("sum.in","r",stdin);
int ncase;
cin>>ncase;
while( ncase-- != 0 )
solve();
return 0;
}
第二题 题目:uva 10047 The Monocycle
一辆独轮车等分分成了5个块,每个块分别有一种颜色,刚开始时独轮车的朝向为北,独轮车的底部为绿色,现在独轮车每秒可以先前移动一个格子,或者把方向移到向左或向右。现在问如何移动独轮车,使得独轮车到达终点且底部颜色为绿色的最短时间。(二维迷宫,若遇到障碍物不能向前移动)
分析:在原来的二维迷宫问题上增加二维,visit[x][y][direction][color],表示格子[x,y]中朝向为direction并以颜色color走过了的就不再重复走。其他的跟二维迷宫基本一样~~
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int X = 26;
const int direction = 4;
const int color = 5;
bool use[X][X][direction][color];
int n,m;
char map[X][X];
int sx,sy,ex,ey;
int dirx[] = {-1,0,1,0};
int diry[] = {0,1,0,-1};
struct node{
int x,y;
int d,c;
int step;
node(){}
node(int _x,int _y,int _d,int _c,int _step):x(_x),y(_y),d(_d),c(_c),step(_step){}
};
bool out(int x,int y){
return x<0||y<0||x>=n||y>=m;
}
int Abs(int x){
return max(x,-x);
}
void bfs(){
memset(use,false,sizeof(use));
queue<node> q;
use[sx][sy][0][0] = true;
q.push(node(sx,sy,0,0,0));
while(q.size()){
node pre = q.front();
q.pop();
int x = pre.x;
int y = pre.y;
int d = pre.d;
int c = pre.c;
if(x==ex&&y==ey&&pre.c==0){
printf("minimum time = %d sec\n",pre.step);
return;
}
pre.step ++;
for(int i=0;i<4;i++){
if(Abs(i-d)==2)
continue;
if(i==d){
int dx = dirx[i]+x;
int dy = diry[i]+y;
int dc = (c+1)%5;
int dd = i;
if(out(dx,dy)||map[dx][dy]=='#'||use[dx][dy][dd][dc])
continue;
use[dx][dy][dd][dc] = true;
q.push(node(dx,dy,dd,dc,pre.step));
}
else{
if(use[x][y][i][c])
continue;
use[x][y][i][c] = true;
q.push(node(x,y,i,c,pre.step));
}
}
}
puts("destination not reachable");
}
int main(){
freopen("sum.in","r",stdin);
int ncase = 0;
while(cin>>n>>m,n||m){
if(ncase)
puts("");
printf("Case #%d\n",++ncase);
for(int i=0;i<n;i++){
scanf("%s",map[i]);
for(int j=0;j<m;j++){
if(map[i][j]=='S'){
sx = i;
sy = j;
}
else if(map[i][j]=='T'){
ex = i;
ey = j;
}
}
}
bfs();
}
return 0;
}
第三题 题目: uva 10054 The Necklace
珠子由两部分组成,每个部分都有颜色,若相同颜色的两个珠子可以串连在一起,现在问能不能把所有的珠子串连在一起。若能够,给出串联的方式
分析:求欧拉回路,把每种颜色看做图上的一个顶点,然后对于每个珠子构造一条无向边,先判断是否有奇数度的顶点,若有的话,欧拉回路不存在,否则DFS一次即可。另外注意图中可能存在多条相同的边(我没测过),所以每次DFS的时候把边删掉一条即可
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int X = 60;
const int n = 50;
int map[X][X];
int id[X];
int m;
void dfs(int x){
for(int y=1;y<=n;y++){
if(map[x][y]>0){
map[x][y] --;
map[y][x] --;
dfs(y);
cout<<y<<" "<<x<<endl;
}
}
}
int main(){
freopen("sum.in","r",stdin);
int ncase,x,y,cnt = 0;
cin>>ncase;
while(ncase--){
if(cnt)
puts("");
printf("Case #%d\n",++cnt);
memset(map,0,sizeof(map));
memset(id,0,sizeof(id));
scanf("%d",&m);
while(m--){
scanf("%d%d",&x,&y);
map[x][y] ++;
map[y][x] ++;
id[x] ++;
id[y] ++;
}
bool ok = true;
for(int i=1;i<=n;i++){
if(id[i]&1){
ok = false;
puts("some beads may be lost");
break;
}
}
if(!ok)
continue;
dfs(x);
}
return 0;
}
第七题 题目:icpc 4287 Proving Equivalences
a能够推出b,b能够推出c,现在问增加多少个关系,使得所有的关系相互等价
分析:不难想到构造出有向图,然后求出图中的强连通分量,缩点之后求max(id,od)。id表示所有入度为0的顶点个数,od表示出度为0的个数,但是得要注意的是当连通块只有一个的时候,答案应该是0(缩点里的所有元素相互等价)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 50005;
const int maxm = 100005;
int po[maxn],tol;
int stack[maxn],dfn[maxn],low[maxn],father[maxn],bcnt,depth,top;
bool instack[maxn];
int id[maxn],od[maxn];
int n,m;
struct node{
int y,next;
}edge[maxm];
void dfs(int x){ //递归实现tarjan算法
low[x] = dfn[x] = ++depth;
instack[x] = true;
stack[++top] = x;
int y;
for(int i=po[x];i;i=edge[i].next){
y = edge[i].y;
if(!dfn[y]){
dfs(y);
low[x] = min(low[x],low[y]);
}
else if(instack[y])
low[x] = min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++bcnt;
do{
y = stack[top--];
instack[y] = false;
father[y] = bcnt;
}while(x!=y);
}
}
void tarjan(){
memset(instack,false,sizeof(instack));
memset(dfn,0,sizeof(dfn));
top = bcnt = depth = 0;
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i);
if(bcnt==1){
puts("0");
return;
}
memset(id,0,sizeof(id));
memset(od,0,sizeof(od));
for(int x=1;x<=n;x++)
for(int j=po[x];j;j=edge[j].next){
int y = edge[j].y;
if(father[x]!=father[y]){
od[father[x]] ++;
id[father[y]] ++;
}
}
int id_cnt = 0,od_cnt = 0;
for(int i=1;i<=bcnt;i++){
if(!id[i])
id_cnt ++;
if(!od[i])
od_cnt ++;
}
cout<<max(id_cnt,od_cnt)<<endl;
}
void add(int x,int y){
edge[++tol].y = y;
edge[tol].next = po[x];
po[x] = tol;
}
int main(){
freopen("sum.in","r",stdin);
int ncase;
cin>>ncase;
while(ncase--){
memset(po,0,sizeof(po));
tol = 0;
int x,y;
cin>>n>>m;
while(m--){
scanf("%d%d",&x,&y);
add(x,y);
}
tarjan();
}
return 0;
}
第八题 题目:uva 11324 The Largest Clique
从图中某点出发,求最远能够一次走过多少个节点
分析:
tarjan求gcc,然后构造出新图,新图是一个dag,对于dag上用dp求出最长路径即可。dp转移方程为dp[x] = size[x] + max(dp[y]); 缩点后有边x到y的边,记忆化搜索就行了,具体看实现代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1005;
const int maxm = 50005;
#define debug puts("here");
int dfn[maxn],low[maxn],stack[maxn],father[maxn],bcnt,top,depth;
bool instack[maxn];
int po[maxn],tol,n,m;
int id[maxn];
int dp[maxn];
int sum[maxn];
vector<int> vec[maxn];
struct node{
int y,next;
}edge[maxm];
void add(int x,int y){
edge[++tol].y = y;
edge[tol].next = po[x];
po[x] = tol;
}
void dfs(int x){ //递归实现tarjan算法
low[x] = dfn[x] = ++depth;
instack[x] = true;
stack[++top] = x;
int y;
for(int i=po[x];i;i=edge[i].next){
y = edge[i].y;
if(!dfn[y]){
dfs(y);
low[x] = min(low[x],low[y]);
}
else if(instack[y])
low[x] = min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++bcnt;
do{
y = stack[top--];
instack[y] = false;
father[y] = bcnt;
}while(x!=y);
}
}
void tarjan(){
memset(instack,false,sizeof(instack));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
top = bcnt = depth = 0;
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i);
}
int f(int x){ //记忆化方法求dag上的最长路径
if(dp[x])
return dp[x];
int ans = 0;
for(int i=0;i<(int)vec[x].size();i++){ //从x的所有边出发,求出最大的路径
int y = vec[x][i];
ans = max(ans,f(y)); //转移方程
}
dp[x] = ans+sum[x];
return dp[x];
}
void dag(){
memset(id,0,sizeof(id));
memset(sum,0,sizeof(sum));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
vec[i].clear();
for(int x=1;x<=n;x++){ //构造新图
for(int j=po[x];j;j=edge[j].next){
int y = edge[j].y;
if(father[x]!=father[y]){
vec[father[x]].push_back(father[y]);
id[father[y]] ++;
}
}
sum[father[x]] ++; //统计每个缩点后的该节点所包含的所有原图的节点数目
}
int ans = 0;
for(int i=1;i<=bcnt;i++)
if(!id[i])
ans = max(f(i),ans);
cout<<ans<<endl;
}
int main(){
freopen("sum.in","r",stdin);
int ncase;
cin>>ncase;
while(ncase--){
cin>>n>>m;
int x,y;
memset(po,0,sizeof(po));
tol = 0;
while(m--){
scanf("%d%d",&x,&y);
add(x,y);
}
tarjan();
dag();
}
return 0;
}
。。。。。