A:Xiangqi(http://poj.org/problem?id=4001)
恶心的模拟题,写的时候一定要小心,枚举棋盘上所有的点判断能否将掉黑帅下一步到达的位置即可,思路很简单但是AC也很难
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 100
using namespace std;
int map[MAXN][MAXN],N; //map[x][y]=kind表示坐标(x,y), 第kind种棋,1->帅 2->车 3->马 4->炮 5->黑帅,竖着x,横着y
int xx[]={1,-1,0,0},yy[]={0,0,1,-1}; //车帅卒的行走方向
int mx[]={-2, -2, -1, 1, 2, 2, 1, -1},my[]= {1, -1, 2, 2, 1, -1, -2, -2} ;//马的行走方向
bool inScope(int x,int y) //判断(x,y)是否在帅的活动范围内
{
if(y<4||y>6||x<1||x>3) return false;
return true;
}
bool inMap(int x,int y) //判断(x,y)是否在棋盘内
{
if(x<1||x>10||y<1||y>9) return false;
return true;
}
int getNum(int x1,int y1,int x2,int y2) //求(x1,y1)到(x2,y2)之间棋子的个数
{
if((x1==x2&&y1==y2)||(x1!=x2&&y1!=y2)) return -1;
if(x1==x2)
{
int cnt=0;
if(y1>y2) swap(y1,y2);
for(int i=y1+1;i<y2;i++)
if(map[x1][i]) cnt++;
return cnt;
}
if(y1==y2)
{
int cnt=0;
if(x1>x2) swap(x1,x2);
for(int i=x1+1;i<x2;i++)
if(map[i][y1]) cnt++;
return cnt;
}
}
bool check(int x,int y) //黑帅在(x,y),可以被打死返回true,不能被打死返回false
{
for(int i=1;i<=10;i++)
for(int j=1;j<=9;j++)
{
switch(map[i][j])
{
case 5:break;
case 1: //帅
{
if(getNum(x,y,i,j)==0)
{
//cout<<x<<' '<<y<<' '<<endl<<i<<' '<<j<<' '<<map[i][j]<<endl;
return true;
}
break;
}
case 2: //车
{
if(getNum(x,y,i,j)==0)
{
//cout<<x<<' '<<y<<' '<<endl<<i<<' '<<j<<' '<<map[i][j]<<endl;
return true;
}
break;
}
case 4: //炮
{
if(getNum(x,y,i,j)==1)
{
//cout<<x<<' '<<y<<' '<<endl<<i<<' '<<j<<' '<<map[i][j]<<endl;
return true;
}
break;
}
case 3: //马
{
for(int dir=0;dir<8;dir++)
{
int tx=i+mx[dir],ty=j+my[dir]; //移动后的马坐标(tx,ty)
if(!inMap(tx,ty)) continue;
if(map[tx][ty]!=5) continue; //根本打不到黑帅
if(mx[dir]==2) if(inMap(i+1,j)&&map[i+1][j]) break; //有棋子挡道
if(mx[dir]==-2) if(inMap(i-1,j)&&map[i-1][j]) break;
if(my[dir]==2) if(inMap(i,j+1)&&map[i][j+1]) break;
if(my[dir]==-2) if(inMap(i,j-1)&&map[i][j-1]) break;
//cout<<x<<' '<<y<<' '<<endl<<i<<' '<<j<<' '<<map[i][j]<<endl;
return true;
}
break;
}
}
}
return false;
}
int main()
{
while(1)
{
memset(map,0,sizeof(map));
int hx,hy,x,y,flag=1,now=0; //黑帅坐标(hx,hy),若黑帅没被将死,flag=0
char s[3];
scanf("%d%d%d",&N,&hx,&hy);
if(N==0&&hx==0&&hy==0) return 0;
map[hx][hy]=5;
for(int i=0;i<N;i++)
{
scanf("%s%d%d",s,&x,&y);
switch(s[0])
{
case 'G':{map[x][y]=1; break;}
case 'R':{map[x][y]=2; break;}
case 'H':{map[x][y]=3; break;}
case 'C':{map[x][y]=4; break;}
}
}
for(int dir=0;dir<4;dir++)
{
int bak; //备份
int tx=hx+xx[dir],ty=hy+yy[dir]; //移动后的黑帅坐标(tx,ty)
if(!inScope(tx,ty)) continue;
bak=map[tx][ty];
map[tx][ty]=5;
map[hx][hy]=0;
if(!check(tx,ty)) {flag=0; break;} //黑帅活下来了
map[tx][ty]=bak;
map[hx][hy]=5;
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
B:Alice's Mooncake Shop(http://poj.org/problem?id=4002)
单调队列,此题还没搞清楚,就不写思路了
#include <iostream>
#include <stdio.h>
#include <string.h>
#define MAXT 3000
using namespace std;
struct Node
{
int num,time; //月饼:时刻为t时费用为num;顾客:时刻为t时要num个月饼
}q[MAXT*4],order[MAXT];
int MonDay[]={0,31,28,31,30,31,30,31,31,30,31,30,31}; //每个月的天数
int mon(char *s)
{
if(strcmp(s,"Jan")==0) return 1;
if(strcmp(s,"Feb")==0) return 2;
if(strcmp(s,"Mar")==0) return 3;
if(strcmp(s,"Apr")==0) return 4;
if(strcmp(s,"May")==0) return 5;
if(strcmp(s,"Jun")==0) return 6;
if(strcmp(s,"Jul")==0) return 7;
if(strcmp(s,"Aug")==0) return 8;
if(strcmp(s,"Sep")==0) return 9;
if(strcmp(s,"Oct")==0) return 10;
if(strcmp(s,"Nov")==0) return 11;
if(strcmp(s,"Dec")==0) return 12;
}
bool isLeapYear(int year) //判定闰年
{
return year%4==0&&(year%100||year%400==0);
}
int getHour(int year,int month,int day,int hour) //日期化为小时
{
int tot=0;
for(int i=2000;i<year;i++)
{
if(isLeapYear(i)) tot+=366; //闰年366天
else tot+=365; //平常365天
}
for(int i=1;i<month;i++)
{
if(isLeapYear(year)&&i==2) tot+=29; //闰年2月29天
else tot+=MonDay[i];
}
tot+=day-1; //加上天数
return tot=tot*24+hour;
}
int main()
{
while(1)
{
char s[10];
int n,m,year,month,day,hour,T,S,cost,h=0,t=0,p=0;
long long int sum=0;
scanf("%d%d",&n,&m);
if(n==0&&m==0) return 0;
for(int i=0;i<n;i++)
{
scanf("%s%d%d%d%d",s,&day,&year,&hour,&order[i].num);
order[i].time=getHour(year,mon(s),day,hour); //存入顾客需求,将日期化为小时
}
scanf("%d%d",&T,&S);
for(int i=0;i<m;i++)
{
scanf("%d",&cost);
while(h<t&&q[t-1].num+S*(i-q[t-1].time)>=cost) t--; //对于第x个订单,其交付日期为j,制作日期为i,保存花费为s,则其总代价为cost[x]+(j-i)*s,维护队尾单调性
q[t].num=cost,q[t++].time=i; //新状态入队
while(p<n&&i==order[p].time) //处理第p个订单
{
while(q[h].time+T<i) h++; //加上保质期后时间还不能到达订单时间(队首的时间太早了),队首出队
sum+=(q[h].num+S*(i-q[h].time))*order[p++].num; //采用时间最早的状态
}
}
printf("%lld\n",sum);
}
return 0;
}
F:Genghis Khan the Conqueror (http://poj.org/problem?id=4006)
最小生成树,不能对于Q次询问跑Q次最小生成树,一定会TLE,需要跑1次最小生成树+1次DFS,求出最小生成树上每两个相邻节点的非树边中的最短边,然后对于Q次询问,每次如果更新的边在树边上时,选择取非树边的最短边或更新后的边去代替生成树上的原有边,即可用O(Q)的复杂度解决
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define INF 1000000000
#define MAXM 3010 //点
#define MAXN 9000100 //边
using namespace std;
vector<int>mst[MAXM];
struct edge
{
int u,v,c;
bool operator<(const edge&rhs)const
{
return c<rhs.c;
}
}edges[MAXN];
int nCount=0; //total edges
int num[MAXM][MAXM],best[MAXM][MAXM];
int f[MAXM];
int inTree[MAXM][MAXM];
int min(int a,int b)
{
if(a<b) return a;
return b;
}
int findSet(int x)
{
if(f[x]==x) return x;
return f[x]=findSet(f[x]);
}
void Union(int a,int b)
{
f[a]=b;
}
double Kruscal(int n,int m)
{
int i,j;
double ans=0;
sort(edges,edges+m);
for(i=0;i<m;i++)
{
int t1=findSet(edges[i].u);
int t2=findSet(edges[i].v);
if(t1!=t2)
{
Union(t1,t2);
ans+=edges[i].c;
mst[edges[i].u].push_back(edges[i].v);
mst[edges[i].v].push_back(edges[i].u);
inTree[edges[i].u][edges[i].v]=1;
inTree[edges[i].v][edges[i].u]=1;
}
}
return ans;
}
int dfs(int rt,int u,int fa)
{
int i,j,s=INF;
for(i=0;i<mst[u].size();i++) //对u结点下的儿子i遍历
{
int v=mst[u][i]; //当前结点u儿子为v
if(v==fa) continue;
int tmp=dfs(rt,v,u);
s=min(s,tmp);
best[u][v]=best[v][u]=min(best[u][v],tmp);
}
if(rt!=fa) //不是
s=min(num[rt][u],s);
return s;
}
int main()
{
int i,j;
int N,M,X,Y,C,Q;
while(1)
{
double ANS=0;
for(i=0;i<MAXM;i++)
{
mst[i].clear();
f[i]=i;
for(j=0;j<MAXM;j++)
{
num[i][j]=best[i][j]=INF;
inTree[i][j]=0;
}
}
scanf("%d%d",&N,&M);
if(N==0&&M==0)
return 0;
for(i=0;i<M;i++)
{
scanf("%d%d%d",&edges[i].u,&edges[i].v,&edges[i].c);
num[edges[i].u][edges[i].v]=num[edges[i].v][edges[i].u]=edges[i].c;
}
double minDis=Kruscal(N,M);
for(i=0;i<N;i++)
dfs(i,i,-1);
scanf("%d",&Q);
for(i=1;i<=Q;i++)
{
scanf("%d%d%d",&X,&Y,&C);
if(!inTree[X][Y]) ANS+=minDis; //该边不在树上,最小生成树不变
else //否则,最小生成树上的边X<->Y选择改成另一最短的边或边权为C的新边
{
ANS+=minDis-num[X][Y]+min(best[X][Y],C);
}
}
printf("%.4lf\n",ANS/(Q*1.0));
}
return 0;
}
G:Flood-it!(http://poj.org/problem?id=4007)
暴搜。比赛的时候以为要用康托展开和逆康拓展开来判重就没做,其实裸IDA*就能过,一定要注意在IDA*搜索前先进行一次flood操作,否则会死循环,郁闷
//IDA*
#include <iostream>
#include <stdio.h>
#include <string.h>
#define MAXN 10
using namespace std;
int map[MAXN][MAXN],xx[4]={1,-1,0,0},yy[4]={0,0,1,-1},status[MAXN][MAXN]; //status[i][j]=1表示(i,j)已被染色,在联通块内,2表示(i,j)没被染色,与联通块相邻,3表示(i,j)未被染色,与联通块不相邻
int N,depth;
bool check(int x,int y) //检查(x,y)是否越界,是返回false
{
if(x>N||x<1||y>N||y<1) return false;
return true;
}
void flood(int x,int y,int col) //以(x,y),颜色为col的格子为起点flood
{
status[x][y]=1; //将当前格子加入联通块
for(int dir=0;dir<4;dir++)
{
int tx=x+xx[dir]; //移动格子到相邻的(tx,ty)
int ty=y+yy[dir];
if(!check(tx,ty)) continue; //越界则跳过
if(status[tx][ty]==1) continue; //在联通块内,跳过
if(map[tx][ty]==col) //是可以染的颜色
flood(tx,ty,col);
else status[tx][ty]=2; //符合条件但是不能染色,则表明(tx,ty)是与联通块相邻的格子
}
}
int get_cnt(int col) //如果对剩余的col色方块染色,最多能染多少个方块
{
int cnt=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
if(status[i][j]==2&&map[i][j]==col) //如果当前格子颜色与col相同且与联通块相邻,对它和相邻的方块染色,由于染色后会更新status数组,所以不会存在重复问题
{
cnt++; //增加
flood(i,j,col);
}
}
return cnt;
}
int h() //估价函数,表示除了联通块的部分,还有多少种颜色,颜色数代表对当前状态未来步数的估计
{
int tot=0;
bool flag[6];
memset(flag,false,sizeof(flag));
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
if(status[i][j]!=1&&!flag[map[i][j]]) //当前点不在联通块内,且该颜色是新种类颜色
{
tot++;
flag[map[i][j]]=true;
}
}
return tot;
}
bool IDAstar(int dep) //在深度为dep的状态下搜索,搜索成功返回true,不成功返回false
{
if(dep==depth) return h()==0; //搜索深度到达目标深度,返回状态是否与目标状态相同
if(dep+h()>depth) return false; //预计深度超了,直接返回false
for(int i=0;i<6;i++) //染第i种颜色
{
int now[MAXN][MAXN]; //now数组用于备份status
memcpy(now,status,sizeof(status));
if(get_cnt(i)==0) //染不了任何一个方块,即无法染色
continue;
if(IDAstar(dep+1)) return true; //加深搜索可行,返回true
memcpy(status,now,sizeof(now)); //将status数组复原
}
return false; //六种颜色都不行,则搜索失败
}
int main()
{
while(1)
{
memset(map,0,sizeof(map));
memset(status,0,sizeof(status));
scanf("%d",&N);
if(N==0) return 0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&map[i][j]);
flood(1,1,map[1][1]); //先操作一次
depth=h();
while(1)
{
if(IDAstar(0)) break; //搜索成功则跳出
depth++; //不成功则增大深度阈值,重新搜索(迭代加深)
}
printf("%d\n",depth); //输出深度
}
return 0;
}