2017-2018 ACM/ICPC, Asia Beijing Regional Contest

E. Cats and Fish

题意:有m条鱼和n只猫,每只猫需要一定的时间吃掉一条鱼。两只猫同时要吃鱼的时候优先让速度快的吃。问第x分钟结束的时候还剩下多少条完整的鱼和不完整的鱼。

题解;把猫吃鱼的时间排个序然后按照题意模拟即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
#define maxn 5005
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int m,n,x;
int a[maxn];
bool flag[maxn];

int main()
{
    while(scanf("%d%d%d",&m,&n,&x)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        sort(a,a+n);
        memset(flag,0,sizeof(flag));
        int num=0,tmp=m;
        for(int i=1;i<=x;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(!flag[j]&&m)
                {
                    flag[j]=1;
                    m--;
                }
                if(i%a[j]==0&&flag[j])flag[j]=0,num++;
            }
        }
        printf("%d %d\n",m,tmp-m-num);
    }
    return 0;
}

F. Secret Poems

题意:把一个字符矩阵从一种变换变成另一种变换

题解:先把原来的串还原出来,再按照规则模拟变换一下。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
#define maxn 105
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n;
char s[maxn][maxn],ans[maxn][maxn];
char tmp[maxn*maxn];

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%s",s[i]);
        bool vis=0;
        int x=0,y=0;
        for(int i=0;i<n*n;i++)
        {
            tmp[i]=s[x][y];
            if(vis)x++,y--;
            else x--,y++;
            if(y>=n&&!vis)x+=2,y--,vis=1;
            else if(x>=n&&vis)y+=2,x--,vis=0;
            else if(x<0)x++,vis=1;
            else if(y<0)y++,vis=0;
        }
        x=0,y=0;
        memset(ans,0,sizeof(ans));
        for(int i=0;i<n*n;)
        {
            while(y<n&&!ans[x][y])
                ans[x][y++]=tmp[i++];
            x++,y--;
            while(x<n&&!ans[x][y])
                ans[x++][y]=tmp[i++];
            x--,y--;
            while(y>=0&&!ans[x][y])
                ans[x][y--]=tmp[i++];
            x--,y++;
            while(x>=0&&!ans[x][y])
                ans[x--][y]=tmp[i++];
            x++,y++;
        }
        for(int i=0;i<n;i++)
            printf("%s\n",ans[i]);
    }
    return 0;
}

G. Liaoning Ship’s Voyage

题意:要从地图的(0,0)移动到(n-1,n-1),且给定了一个三角形区域不能经过,求最小的步数。

题解:关键就是在走每一步的时候判断路径有没有和给定的三角形相交。判断比较麻烦,路径在三角形的边上或者只经过某一个顶点的情况是可以通行的,只要不经过三角形的内部即可。考虑到状态规模不大且单条路径不长,可以在路径上等分100个点,只要这些点都不在三角形的内部即可认为路径不经过三角形的内部。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 25
#define INF 0x3f3f3f3f
#define eps 1e-8
const double pi=acos(-1.0);
using namespace std;
typedef long long ll;

struct point
{
    double x,y;
    point(double x=0,double y=0):x(x),y(y){}
    double an()
    {
        if(x==0)
        {
            if(y>0)return pi/2;
            else if(y<0)return 1.5*pi;
            else return 0.0;
        }
        if(y>0)return atan(y/x);
        else return atan(y/x)+pi;
    }
}e[5];
double Min(double a,double b){return a<b?a:b;}
double Max(double a,double b){return a>b?a:b;}
//bool cmp(point a,point b){return a.an()<b.an();}
point operator + (point a,point b){return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b){return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p){return point(a.x*p,a.y*p);}
point operator / (point a,double p){return point(a.x/p,a.y/p);}
bool operator < (const point& a,const point& b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
int dcmp(double x){if(fabs(x)<eps)return 0;else return x<0?-1:1;}
bool operator == (const point& a,const point& b){return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;}
bool operator != (const point& a,const point& b){return dcmp(a.x-b.x)!=0||dcmp(a.y-b.y)!=0;}
point rotat(point a,double rad){return point(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}//向量逆时针旋转
double dot(point a,point b){return a.x*b.x+a.y*b.y;}//点积
double length(point a){return sqrt(dot(a,a));}//取模
double angle(point a,point b){return acos(dot(a,b)/length(a)/length(b));}//夹角
double cross(point a,point b){return a.x*b.y-a.y*b.x;}//叉积
bool onseg(point a,point b,point c){return dcmp(cross(b-a,c-a))==0&&dcmp(dot(b-a,c-a))<0;}//判断a是否在bc上
double dis_line(point p,point a,point b){point v1=b-a,v2=p-a;return fabs(cross(v1,v2))/length(v1);}//p到直线ab的距离
point line_project(point p,point a,point b){point v=b-a;return a+v*(dot(v,p-a)/dot(v,v));}//p在直线ab的投影
point line_cross(point p,point v,point q,point w){double t=cross(w,p-q)/cross(v,w);return p+v*t;}//p+tv与q+tw的交点
double area(point* p,int n){double ans=0;for(int i=1;i<n-1;i++)ans+=cross(p[i]-p[0],p[i+1]-p[0]);return ans/2;}//多边形面积
double dis_seg(point p,point a,point b)//p到线段ab的距离
{
    if(a==b)return length(p-a);
    point v1=b-a,v2=p-a,v3=p-b;
    if(dcmp(dot(v1,v2))>0)return length(v2);
    else if(dcmp(dot(v1,v3))>0)return length(v3);
    else return fabs(cross(v1,v2))/length(v1);
}
bool seg_cross(point a1,point b1,point a2,point b2)//判断线段a1b1与a2b2是否相交
{
    double c1=cross(b1-a1,a2-a1),c2=cross(b1-a1,b2-a1);
    double c3=cross(b2-a2,a1-a2),c4=cross(b2-a2,b1-a2);
    if(onseg(a1,a2,b2)||onseg(b1,a2,b2)||onseg(a2,a1,b1)||onseg(b2,a1,b1))return true;
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
int n,m;
char maze[maxn][maxn];
int flag[maxn][maxn];
int pos[8][2]={1,0,0,1,-1,0,0,-1,1,1,1,-1,-1,-1,-1,1};
struct node
{
    int x,y;
    int num;
};

bool check(int x,int y)
{
    if(x>=0&&y>=0&&x<n&&y<n&&!flag[x][y])
        return true;
    return false;
}

bool inside(point p)
{
    double t1=cross(p-e[0],e[1]-p);
    double t2=cross(p-e[1],e[2]-p);
    double t3=cross(p-e[2],e[0]-p);
    int s1=dcmp(t1),s2=dcmp(t2),s3=dcmp(t3);
    if(s1==0||s2==0||s3==0)return false;
    if(s1>0&&s2>0&&s3>0)return true;
    if(s1<0&&s2<0&&s3<0)return true;
    return false;
}

bool judge(point a,point b)
{
    if(inside(b))return false;
    for(int i=0;i<100;i++)
    {
        point pa;
        pa.x=a.x+i*(b.x-a.x)/100;
        pa.y=a.y+i*(b.y-a.y)/100;
        if(pa==e[0]||pa==e[1]||pa==e[2])continue;
        if(inside(pa))
            return false;
    }
    return true;
}

int bfs()
{
    queue<node>q;
    node st,now;
    memset(flag,0,sizeof(flag));
    st.x=st.y=st.num=0;
    q.push(st);
    flag[0][0]=1;
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        if(now.x==n-1&&now.y==n-1)return now.num;
        for(int i=0;i<8;i++)
        {
            int nx=now.x+pos[i][0];
            int ny=now.y+pos[i][1];
            if(!check(nx,ny)||maze[nx][ny]=='#')continue;
            point sta=point(1.0*now.x,1.0*now.y);
            point en=point(1.0*nx,1.0*ny);
            if(judge(sta,en))
            {
                flag[nx][ny]=1;
                st.x=nx,st.y=ny;
                st.num=now.num+1;
                //cout<<nx<<" "<<ny<<endl;
                q.push(st);
            }
            else flag[nx][ny]=2;
        }
    }
    return -1;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<3;i++)
            scanf("%lf%lf",&e[i].x,&e[i].y);
        for(int i=0;i<n;i++)
            scanf("%s",maze[n-i-1]);
        printf("%d\n",bfs());
    }
    return 0;
}

H. Puzzle Game

题意:给定一个矩阵,现可以将矩阵中的一个元素变为p,求变换之后的最大子矩阵的值。

题解:用dp求最大子矩阵的时间复杂度是O(n^3),如果枚举变换的位置,时间复杂度就会达到O(n^5)

如果要修改的话肯定是对最大子矩阵内的某个元素进行修改,否则最大子矩阵之和不会变化。因此我们需要预处理出最大子矩阵的位置。遍历最大子矩阵中的点,修改一个点之后新矩阵的最大子矩阵是:

Max(上方、下方、左方、右方最大子矩阵,包含此点的新最大子矩阵)

因此还需要预处理出每行上方、下方的最大子矩阵和每列左方、右方的最大子矩阵。

如果存在多个最大子矩阵,维护任意一个即可。因为如果点被多个最大子矩阵所包含,那么修改这个点对这些最大子矩阵的效果是相同的。如果有不包含这个点的最大子矩阵,我们在预处理的时候会计算出来。

预处理的时间复杂度为O(n^3),维护最大子矩阵的时间复杂度为O(n^2),总的时间复杂度为O(n^3)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
#define maxn 160
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,p;
int l[maxn],r[maxn],u[maxn],d[maxn];
int sum[maxn][maxn],a[maxn][maxn];

int main()
{
    while(scanf("%d%d%d",&n,&m,&p)!=EOF)
    {
        memset(sum,0,sizeof(sum));
        for(int i=0;i<=n+2;i++)
            l[i]=r[i]=u[i]=d[i]=-INF;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
            }
        }
        int maxsum=-INF;
        for(int i=n;i>=1;i--)
        {
            for(int j=i;j<=n;j++)
            {
                int x=0,ans=-INF;
                for(int k=1;k<=m;k++)
                {
                    int w=sum[j][k]-sum[i-1][k]-sum[j][k-1]+sum[i-1][k-1];
                    if(x<0)x=w;
                    else x+=w;
                    ans=max(ans,x);
                }
                d[i-1]=max(d[i-1],max(d[i],ans));
                maxsum=max(maxsum,ans);
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                int x=0,ans=-INF;
                for(int k=1;k<=m;k++)
                {
                    int w=sum[i][k]-sum[j-1][k]-sum[i][k-1]+sum[j-1][k-1];
                    if(x<0)x=w;
                    else x+=w;
                    ans=max(ans,x);
                }
                u[i+1]=max(u[i+1],max(u[i],ans));
            }
        }
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=i;j++)
            {
                int x=0,ans=-INF;
                for(int k=1;k<=n;k++)
                {
                    int w=sum[k][i]-sum[k][j-1]-sum[k-1][i]+sum[k-1][j-1];
                    if(x<0)x=w;
                    else x+=w;
                    ans=max(ans,x);
                }
                l[i+1]=max(l[i+1],max(l[i],ans));
            }
        }
        for(int i=m;i>=1;i--)
        {
            for(int j=i;j<=m;j++)
            {
                int x=0,ans=-INF;
                for(int k=1;k<=n;k++)
                {
                    int w=sum[k][j]-sum[k][i-1]-sum[k-1][j]+sum[k-1][i-1];
                    if(x<0)x=w;
                    else x+=w;
                    ans=max(ans,x);
                }
                r[i-1]=max(r[i-1],max(r[i],ans));
            }
        }
        int sx,sy,ex,ey,st;
        bool vis=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                int x=0;
                st=1;
                for(int k=1;k<=m;k++)
                {
                    int w=sum[j][k]-sum[j][k-1]-sum[i-1][k]+sum[i-1][k-1];
                    if(x<0)
                    {
                        st=k;
                        x=w;
                    }
                    else x+=w;
                    if(x==maxsum)
                    {
                        sx=i,sy=st,ex=j,ey=k;
                        vis=1;
                        break;
                    }
                }
                if(vis)break;
            }
            if(vis)break;
        }
        int Ans=maxsum;
        for(int i=sx;i<=ex;i++)
        {
            for(int j=sy;j<=ey;j++)
                Ans=min(Ans,max(maxsum-a[i][j]+p,max(max(l[j],r[j]),max(u[i],d[i]))));
        }
        printf("%d\n",Ans);
    }
    return 0;
}

J. Pangu and Stones

题意:合并石子,但每次只能合并连续的石子堆且堆数必须在给定的区间之内。求合并的最小花费。

题解:区间dp,用dp[i][j][k]表示将从第i堆到第j堆石子合并成k堆的最小花费。则状态转移方程为:

K=1:dp[i][j][1]=min(dp[i][p][x-1]+dp[p+1][j][1]+sum(i,j)),i<=p<j,l<=x<=r

K>1:dp[i][j][k]=min(dp[i][p][k-1]+dp[p+1][j][1]),i<=p<j

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
#define maxn 105
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,l,r,a[maxn];
ll dp[maxn][maxn][maxn];
int sum[maxn];

int main()
{
    while(scanf("%d%d%d",&n,&l,&r)!=EOF)
    {
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
            {
                for(int k=1;k<=n;k++)dp[i][j][k]=INF;
                dp[i][j][0]=0;
            }
            for(int j=i;j<=n;j++)
                dp[i][j][j-i+1]=0;
        }
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int p=1;p<n;p++)
        {
            for(int i=1;i+p<=n;i++)
            {
                for(int j=i;j<i+p;j++)
                {
                    for(int k=l;k<=r;k++)
                        dp[i][i+p][1]=min(dp[i][i+p][1],dp[i][j][k-1]+dp[j+1][i+p][1]+sum[i+p]-sum[i-1]);
                }
                for(int j=i;j<i+p;j++)
                {
                    for(int k=2;k<=p;k++)
                        dp[i][i+p][k]=min(dp[i][i+p][k],dp[i][j][k-1]+dp[j+1][i+p][1]);
                }
            }
        }
        if(dp[1][n][1]==1LL*INF)
            printf("0\n");
        else printf("%lld\n",dp[1][n][1]);
    }
    return 0;
}

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页