USACO 2.4
USACO 2.4.1
题解:
模拟。
用一个6维数组储存农夫与奶牛当前状态是否出现过,若出现过则表明出现循环,直接输出0,f[农夫x][农夫y][农夫方向][奶牛x][奶牛y][奶牛方向]。
最后注意转弯要算一步。
代码:
/* ID:m1599491 PROB:ttwo LANG:C++ */ #include<cmath> #include<cstdio> #include<cstring> #include<iostream> using namespace std; string s[12]; int i,j,xf,xc,yf,yc; bool sta[11][11][5][11][11][5]={0}; const int dx[4]={-1,0,1,0}; const int dy[4]={0,1,0,-1}; bool obs(int x,int y,int dir) { dir--; if (x+dx[dir]>10 || x+dx[dir]<=0 || y+dy[dir]>=10 || y+dy[dir]<=-1) return 1; if (s[x+dx[dir]][y+dy[dir]]=='*') return 1; return 0; } main() { freopen("ttwo.in","r",stdin); freopen("ttwo.out","w",stdout); for (i=1; i<=10; i++) { cin>>s[i]; for (j=0; j<s[i].size(); j++) { if (s[i][j]=='F') {xf=i;yf=j;s[i][j]='.';} if (s[i][j]=='C') {xc=i;yc=j;s[i][j]='.';} } } int dirf=1,dirc=1,ans=0,hh=0; while (1==1) { if (xf==xc && yf==yc) {printf("%d\n",ans);break;} if (sta[xf][yf][dirf][xc][yc][dirc]) {printf("0\n");return 0;} sta[xf][yf][dirf][xc][yc][dirc]=1; int a=dirf,b=dirc; if (obs(xf,yf,dirf)) {if (dirf==4) dirf=1; else dirf++;} if (a==dirf) {xf+=dx[dirf-1];yf+=dy[dirf-1];} if (obs(xc,yc,dirc)) {if (dirc==4) dirc=1; else dirc++;} if (b==dirc) {xc+=dx[dirc-1];yc+=dy[dirc-1];} ans++; } return 0; }
USACO 2.4.2
题解:
其实就是一个迷宫,虽然有两个出口,但只需要一遍BFS就够了,将两个出口的坐标同时加入队列就行了,dis[i][j]表示从(i,j)走出迷宫的最短距离。
最后整个图扫一遍,更新答案。
代码:
/* ID:m1599491 PROB:maze1 LANG:C++ */ #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<algorithm> #include<iostream> #define MAXN 210 using namespace std; const int dx[4]={-1,0,1,0}; const int dy[4]={0,1,0,-1}; string s[MAXN]; int dis[MAXN][MAXN],f[10010][2],g[MAXN][MAXN]; int i,j,front,tail=1,w,h,fx,fy; void bfs(int x1,int y1,int x2,int y2) { int i,j,x,y; memset(dis,-1,sizeof(dis)); dis[x1][y1]=dis[x2][y2]=0; f[++front][1]=x1;f[front][2]=y1; f[++front][1]=x2;f[front][2]=y2; while (tail<=front) { x=f[tail][1];y=f[tail][2]; for (i=0; i<=3; i++) { fx=x+dx[i];fy=y+dy[i]; if (fx<0 || fx>=h || fy<0 || fy>=w) continue; if (s[fx][fy]!=' ') continue; if (dis[fx][fy]==-1 || dis[fx][fy]>dis[x][y]) { f[++front][1]=fx;f[front][2]=fy; if (fx%2==0 || fy%2==0) dis[fx][fy]=dis[x][y]; else dis[fx][fy]=dis[x][y]+1; } } tail++; } } main() { freopen("maze1.in","r",stdin); freopen("maze1.out","w",stdout); scanf("%d%d",&w,&h);getline(cin,s[1]); w=w*2+1;h=h*2+1; int x1=-1,y1=-1,x2,y2; for (i=0; i<=h-1; i++) getline(cin,s[i]); for (i=0; i<h; i++) for (j=0; j<w; j++) { if (s[i][j]==' ' && (i==0 || j==0 || i==h-1 || j==w-1)) if (x1==-1) {x1=i;y1=j;} else {x2=i;y2=j;} } bfs(x1,y1,x2,y2); int ans=0; for (i=0; i<h; i++) for (j=0; j<w; j++) ans=max(ans,dis[i][j]); printf("%d\n",ans); return 0; }
USACO 2.4.3
题解:
这题剧毒,难点就在于题意。
先给你N个点的坐标,再给你一个邻接矩阵,若(i,j)=1,则i牧区与j牧区有一条通路(无向)。
若几个牧区能互相到达则成为一个牧场,一个牧场的直径就是这个牧场里面,所有牧区两两之间的最短距离的最大值。
现在有若干个牧场,要求你在任意两个牧场里面的任意两个牧区i,j(i!=j)连一条道路,使得合并之后,所有牧场里面,半径的最大值最小,要你求出这条半径的长度。(这就是题意
由于N只有150,很容易想到的就是Floyd,但无脑Floyd根本卡不过大数据,就像利用Floyd生成原始情况,然后每条没有连接的边枚举过去,重新Floyd计算一下最大直径。经测试,这种想法在第4个测试数据上用时近半秒,然后第5个点你等了2分钟还没有出解。
接下来想优化,先用并查集将同一个牧场里的点合并,再用勾股定理预处理出所有点两两之间的距离dis[i][j],然后跑Floyd,注意不同牧场的点不能在Floyd里面计算,保证i,j,k三点在同一牧场。
再接着,处理出数组mxdis[i],表示i牧区与i牧区所在的牧场的各个牧区的最短距离的最大值。将所有mxdis[i]扫一遍,利用并查集便能求出i牧区所在牧场的直径dia[F[i]],并求出最长的直径MAX。
最后将每对不在同个牧场的牧区枚举一遍,假设i牧区在甲牧场,j牧区在乙牧场,那么合并之后的牧场丙的直径就是max(dia[F[i]],dia[F[j]],mxdis[i]+dis[i][j]+mxdis[j]),F[i]表示i牧区所在的牧场。由于当前的答案为max(新牧区的直径,其他所有牧区的直径),那么直接就是max(MAX,mxdis[i]+dis[i][j]+mxdis[j]),则ans=min(ans,max(MAX,mxdis[i]+dis[i][j]+mxdis[j]))。
代码:
/* ID:m1599491 PROB:cowtour LANG:C++ */ #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MX 200 using namespace std; string s; int i,j,k,n,f[MX][MX],x[MX],y[MX],F[MX]; double dis[MX][MX],mxdis[MX],dia[MX],MAX=-1,ans=0x7fffffff; double min(double x,double y) {return x<y?x:y;} double max(double x,double y) {return x>y?x:y;} double Py(int x1,int y1,int x2,int y2) {return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));} int GF(int x) {if (F[x]!=x) F[x]=GF(F[x]);return F[x];} int MG(int i,int j) {i=GF(i);j=GF(j);if (i!=j) F[i]=j;} main() { freopen("cowtour.in","r",stdin); freopen("cowtour.out","w",stdout); scanf("%d",&n); for (i=1; i<=n; i++) F[i]=i; for (i=1; i<=n; i++) scanf("%d%d",&x[i],&y[i]);getline(cin,s); for (i=1; i<=n; i++) { getline(cin,s); for (j=0; j<s.size(); j++) if (s[j]=='1') { f[i][j+1]=1; MG(i,j+1); dis[i][j+1]=Py(x[i],y[i],x[j+1],y[j+1]); } } for (i=1; i<=n; i++) F[i]=GF(i); for (i=1; i<=n; i++) for (j=1; j<=n; j++) if (F[i]!=F[j]) dis[i][j]=Py(x[i],y[i],x[j],y[j]); for (k=1; k<=n; k++) for (i=1; i<=n; i++) { if (F[k]!=F[i]) continue; for (j=1; j<=n; j++) { if (i==j || F[i]!=F[j]) continue; if (dis[i][k]!=0 && dis[k][j]!=0) if (dis[i][j]==0 || dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j]; } } for (i=1; i<=n; i++) for (j=1; j<=n; j++) { if (F[i]==F[j]) continue; else dis[i][j]=Py(x[i],y[i],x[j],y[j]); } for (i=1; i<=n; i++) for (j=1; j<=n; j++) if (F[i]==F[j]) mxdis[i]=max(mxdis[i],dis[i][j]); for (i=1; i<=n; i++) dia[F[i]]=max(dia[F[i]],mxdis[i]),MAX=max(MAX,dia[F[i]]); for (i=1; i<=n; i++) for (j=i+1; j<=n; j++) if (F[i]!=F[j]) { double T=mxdis[i]+dis[i][j]+mxdis[j]; ans=min(ans,max(T,MAX)); } printf("%.6f\n",ans); }
USACO 2.4.4
题解:
输入输出感觉有点麻烦,然后就是SBSPFA了。
代码:
/* ID:m1599491 PROB:comehome LANG:C++ */ #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #define INF 0x7fffffff using namespace std; string s; char c1,c2; int a[100][100],b[100][100],p[100],dis[100],n,m,i,j,front,tail,k,now,t,x,y,c,ans=INF; bool v[100]; main() { freopen("comehome.in","r",stdin); freopen("comehome.out","w",stdout); scanf("%d",&n);getchar(); for (i=1; i<=n; i++) { getline(cin,s); c1=s[0];c2=s[2];c=0; for (j=4; j<s.size(); j++) c=c*10+(s[j]-48); if (c1<91) x=c1-64+26; else if (c1>96) x=c1-96; if (c2<91) y=c2-64+26; else if (c2>96) y=c2-96; b[x][0]++;b[x][b[x][0]]=y;if (a[x][y]) a[x][y]=min(a[x][y],c); else a[x][y]=c; b[y][0]++;b[y][b[y][0]]=x;if (a[y][x]) a[y][x]=min(a[y][x],c); else a[y][x]=c; } memset(v,0,sizeof(v)); for (i=1; i<=52; i++) dis[i]=INF; dis[52]=0;front=1;tail=1;p[1]=52;v[52]=1; while (front<=tail) { now=p[front]; for (i=1; i<=b[now][0]; i++) if (dis[b[now][i]]>dis[now]+a[now][b[now][i]]) { dis[b[now][i]]=dis[now]+a[now][b[now][i]]; if (!v[b[now][i]]) { p[++tail]=b[now][i]; v[b[now][i]]=1; } } front++;v[now]=0; } for (i=27; i<=51; i++) if (dis[i]<ans) { ans=dis[i]; k=i; } printf("%c %d\n",k-26+64,ans); }
USACO 2.3.5
题解:
简单模拟一波。
先直接输出能整除的情况。
接着就先输出整数部分与小数点,并统计整数部分+小数点的位数,方便输出。
last[rem]表示rem这个余数最先在哪里出现,如果遇到last[rem]>0那么说明出现循环节,如果rem=0的话就说明除尽了,然后随便搞搞输出就行了。
代码:
/* ID:m1599491 PROG:fracdec LANG:C++ */ #include<cmath> #include<cstring> #include<cstdio> #include<algorithm> #include<iostream> using namespace std; int i,j,x,y,z,rem,quo,tot,bg,ed,last[100010]={0},N; char c[100010]; bool loop=0; int gcd(int x,int y) {if (y==0) return x;return gcd(y,x%y);} int num(int x) { int k=0,div=x; while (div) {div=div/10;k++;} return k; } main() { freopen("fracdec.in","r",stdin); freopen("fracdec.out","w",stdout); scanf("%d%d",&x,&y); if (x%y==0) {printf("%d.0\n",x/y);return 0;} z=gcd(x,y);x=x/z;y=y/z; if (x/y==0) N=2; else N=num(x/y)+1; printf("%d.",x/y); rem=x%y; while (true) { tot++; last[rem]=tot; c[tot]=rem*10/y+48; rem=rem*10%y; if (!rem) break; if (last[rem]) {bg=last[rem];ed=tot-1;loop=1;break;} } for (i=1; i<=tot; i++) { if (loop && i==bg) {if (N%76==0) printf("\n");printf("(");N++;} printf("%c",c[i]); N++; if (N%76==0) printf("\n"); } if (loop) {if (N%76==0) printf("\n");printf(")");}printf("\n"); return 0; }