【USACO2.4】解题报告

前言

好久没有刷USACOUSACO的题目了。这个合集也咕咕咕了。
最近终于从JZOJJZOJ和比赛中脱过身来,补一下坑。
其实前面几道题好早就AA了,主要是最后一道模拟一直不想做。做了之后才发现挺简单的。也就用了洛谷的8个下载测试点而已。
本章主要考察搜索+最短路。还是处于基础阶段。
USACOUSACOhttp://train.usaco.org/


2.4.2.The Tamworth Two

思路:

这道题没有什么好说的。直接按照题目所说的方法搜索下去即可。
遇见障碍就拐弯,直到相遇为止。
clock()clock()计算时间,若即将超时就输出00,极大的几率是无解的。


代码:

#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

思路:

先求出任意两点之间距离,在求两个联通快内的最长路径,然后枚举连接哪两个点i,ji,j,那么很明显答案就是max(max(距离ii最远点距离+i+ijj的距离++距离jj最远点距离,第一个联通块内的最长路径,第二个联通块内的最长路径))
可以用FloydFloyd+并查集求。


代码:

#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

思路:

最短路模板,没什么好说的。
不卡SPFASPFA好评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

思路:

当除法进行到一段时间后,出现了商和余数与曾经的某一次完全一样的情况,那么就说明找到循环节了。
那么就模拟下去,注意特判一些恶心的情况。
**题我都不知道该说什么了。
懒得打hashhash,用了mapmap水过去。


代码:

#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);
}
posted @ 2019-01-22 15:37  全OI最菜  阅读(99)  评论(0编辑  收藏  举报