【USACO2.4】解题报告
前言
好久没有刷的题目了。这个合集也咕咕咕了。
最近终于从和比赛中脱过身来,补一下坑。
其实前面几道题好早就了,主要是最后一道模拟一直不想做。做了之后才发现挺简单的。也就用了洛谷的8个下载测试点而已。
本章主要考察搜索+最短路。还是处于基础阶段。
:http://train.usaco.org/
2.4.2.The Tamworth Two
思路:
这道题没有什么好说的。直接按照题目所说的方法搜索下去即可。
遇见障碍就拐弯,直到相遇为止。
用计算时间,若即将超时就输出,极大的几率是无解的。
代码:
#include <cstdio>
#include <ctime>
#include <iostream>
using namespace std;
const int N=11;
const int dx[]={0,-1,0,1,0};
const int dy[]={0,0,1,0,-1};
char map[N][N];
int x,y,x1,y1,x2,y2,p1,p2,sum,Time;
//北1
//东2
//南3
//西4
int main()
{
Time=clock(); //计算时间
for (int i=1;i<=10;i++)
{
cin>>map[i]+1;
for (int j=1;j<=10;j++)
{
if (map[i][j]=='F')
x1=i,y1=j;
if (map[i][j]=='C')
x2=i,y2=j; //记录位置
}
}
p1=p2=1;
if (x1==x2&&y1==y2)
return !printf("0\n");
sum=1;
while (clock()-Time<950) //超时就退出
{
x=x1+dx[p1],y=y1+dy[p1];
if (x>0&&y>0&&x<11&&y<11&&map[x][y]!='*')
{
x1=x;
y1=y;
}
else p1=p1%4+1;
x=x2+dx[p2],y=y2+dy[p2];
if (x>0&&y>0&&x<11&&y<11&&map[x][y]!='*')
{
x2=x;
y2=y;
}
else p2=p2%4+1;
if (x1==x2&&y1==y2) return !printf("%d\n",sum);
sum++;
}
printf("0\n");
return 0;
}
2.4.3.Shortest Paths
思路:
暴力广搜,直到走出去为止。
注意细节。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int dx[]={0,0,0,-1,1};
const int dy[]={0,-1,1,0,0};
int n,m,ans=1;
char map[310][310];
bool vis[310][310];
struct Map //记录地图
{
queue<int> x,y; //广搜队列
}q;
queue<int> dis;
void bfs() //广搜模板
{
while (q.x.size())
{
int x=q.x.front();
int y=q.y.front();
q.x.pop();
q.y.pop();
for (int i=1;i<=4;i++)
{
int xx=x+2*dx[i],yy=y+2*dy[i];
if (map[x+dx[i]][y+dy[i]]!=' ') continue;
if (vis[xx][yy]) continue;
if (xx<1||xx>2*n||yy<1||yy>2*m) continue;
vis[xx][yy]=1;
q.x.push(xx);
q.y.push(yy);
dis.push(dis.front()+1);
ans=max(ans,dis.back());
}
dis.pop();
}
}
int main()
{
scanf("%d%d\n",&m,&n);
for (int i=1;i<=n*2+1;i++)
gets(map[i]+1);
for (int i=1;i<=n;i++)
{
if (map[i*2][1]==' ')
q.x.push(i*2),q.y.push(2),dis.push(1);
if (map[i*2][m*2+1]==' ')
q.x.push(i*2),q.y.push(m*2),dis.push(1); //注意建图细节
}
for (int i=1;i<=m;i++)
{
if (map[1][i*2]==' ')
q.x.push(2),q.y.push(i*2),dis.push(1);
if (map[n*2+1][i*2]==' ')
q.x.push(n*2),q.y.push(i*2),dis.push(1);
}
bfs();
printf("%d\n",ans);
getchar();getchar();
return 0;
}
2.4.4.Cow Tours
思路:
先求出任意两点之间距离,在求两个联通快内的最长路径,然后枚举连接哪两个点,那么很明显答案就是距离最远点距离到的距离距离最远点距离,第一个联通块内的最长路径,第二个联通块内的最长路径。
可以用+并查集求。
代码:
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const int N=200;
const double Inf=1e9;
int n,x[N],y[N],map[N][N],father[N];
double dis[N][N],sum1,sum2,ans,maxn[N];
double get_dis(double x1,double x2,double y1,double y2) //求距离
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int find(int x) //并查集
{
return x==father[x]?x:father[x]=find(father[x]);
}
int main()
{
ans=Inf;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
father[i]=i;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%1d",&map[i][j]);
if (map[i][j]==1)
{
dis[i][j]=get_dis(x[i],x[j],y[i],y[j]);
father[find(j)]=find(i);
}
else dis[i][j]=Inf+1.0;
}
for (int k=1;k<=n;k++) //Floyd
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j&&j!=k&&k!=i)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) //块内最长路径
if (find(i)==find(j)&&i!=j)
maxn[find(i)]=max(maxn[find(i)],dis[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) //选择ij连边
if (i!=j&&find(i)!=find(j))
{
sum1=0;
sum2=0;
for (int k=1;k<=n;k++)
{
if (find(i)==find(k)&&i!=k)
sum1=max(sum1,dis[i][k]);
if (find(j)==find(k)&&j!=k)
sum2=max(sum2,dis[j][k]);
}
ans=min(ans,max(sum1+sum2+get_dis(x[i],x[j],y[i],y[j]),max(maxn[find(i)],maxn[find(j)])));
}
printf("%6lf\n",ans);
return 0;
}
2.4.5.Bessie Come Home
思路:
最短路模板,没什么好说的。
不卡好评XD。
代码:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N=100;
const int M=10010;
int n,x,head[N],tot,vis[N],dis[N],ans,minn;
char ch1,ch2;
struct edge
{
int next,to,dis;
}e[M*2];
int pos(char c) //给字母编号
{
if (c>='A'&&c<='Z') return c-'A'+1;
return c-'a'+27;
}
void add(int from,int to,int dis)
{
e[++tot].to=to;
e[tot].dis=dis;
e[tot].next=head[from];
head[from]=tot;
}
void spfa()
{
queue<int> q;
q.push(26);
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[26]=0;
vis[26]=1;
while (q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (dis[v]>dis[u]+e[i].dis)
{
dis[v]=dis[u]+e[i].dis;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>ch1>>ch2>>x;
add(pos(ch1),pos(ch2),x);
add(pos(ch2),pos(ch1),x);
}
spfa();
minn=2147483647;
for (int i=1;i<=25;i++)
if (head[i]!=-1&&dis[i]<minn)
{
minn=dis[i];
ans=i;
}
cout<<(char)(ans+'A'-1)<<" "<<minn<<endl;
return 0;
}
2.4.6.Fractions to Decimals
思路:
当除法进行到一段时间后,出现了商和余数与曾经的某一次完全一样的情况,那么就说明找到循环节了。
那么就模拟下去,注意特判一些恶心的情况。
题我都不知道该说什么了。
懒得打,用了水过去。
代码:
#include <cstdio>
#include <map>
#include <algorithm>
#define mp make_pair
using namespace std;
int a,b,ans[200010],s,n,k;
map<pair<int,int>,int> p;
int len(int x) //求整数部分长度
{
if (x>9) return len(x/10)+1;
return 1;
}
int main()
{
scanf("%d%d",&a,&b);
if (__gcd(a,b)==b)
{
printf("%d.0\n",a/b);
return 0;
}
s=len(a/b)+1; //+1是加上小数点的长度
printf("%d.",a/b);
a%=b;
while (1)
{
n++;
if (!a)
{
for (int i=1;i<n;i++) //能除尽
{
s++;
printf("%d",ans[i]);
if (s%76==0) putchar(10);
}
putchar(10);
return 0;
}
a*=10;
if (p[mp(a/b,a%b)]) //判断是否出现过这种情况
{
k=p[mp(a/b,a%b)];
break;
}
p[mp(a/b,a%b)]=n;
ans[n]=a/b;
a%=b;
}
for (int i=1;i<n;i++)
{
if (i==k)
{
putchar('(');
s++;
if (s%76==0) putchar(10);
}
printf("%d",ans[i]);
s++;
if (s%76==0) putchar(10);
}
putchar(')');
putchar(10);
}