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