2017-2018 ACM/ICPC, Asia Beijing Regional Contest

E. Cats and Fish

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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#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

F. Secret Poems

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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#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&&!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=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

G. Liaoning Ship’s Voyage

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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 ab?a:b;}
//bool cmp(point a,point b){return a.an()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&&x0&&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()
{
    queueq;
    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<

H. Puzzle Game

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

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

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

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

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

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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#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

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

#include
#include
#include
#include
#include
#include
#include
#include
#include
#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

你可能感兴趣的:(ACM-ICPC)