1. 程式人生 > >2017-2018 ACM/ICPC, Asia Beijing Regional Contest

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;
}