2019 ICPC Malaysia National 题解
A - Mental Rotation(模拟)
思路:
只要发现旋转的规律就好了
就是向右旋转的时候,矩阵整体旋转,之后里面的图形成为逆时针旋转$90°$的图形
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=5005; char a[maxn][maxn], a1[maxn][maxn], str[maxn]; int n; void Right() { for(int i=1; i<=n; i++) for(int j=1; j<=n; j++){ if(a[i][j]=='^') a1[j][n-i+1]='>'; else if(a[i][j]=='v') a1[j][n-i+1]='<'; else if(a[i][j]=='<') a1[j][n-i+1]='^'; else if(a[i][j]=='>') a1[j][n-i+1]='v'; else a1[j][n-i+1] ='.'; } for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) a[i][j]=a1[i][j]; } int main() { int l, r; scanf("%d%s",&n,str); for(int i=1; i<=n; i++) scanf("%s", a[i]+1); l=0;r=0; for(int i=0; i<strlen(str); i++){ if(str[i]=='L') l++; else r++; } l%=4; r%=4; r=(r-l+4)%4; while(r--) Right(); for(int i=1; i<=n; i++) printf("%s\n", a[i]+1); return 0; }
B - SpongeBob SquarePants (签到题)
#include<iostream> #include<algorithm> using namespace std; int main() { int t; cin>>t; while(t--){ int a,b; cin>>a>>b; if(a==b) cout<<"YES"<<endl; else cout<<"NO"<<endl; } }
C - I Don't Want To Pay For The Late Jar!(签到题)
#include<iostream> #include<algorithm> using namespace std; const int inf = 0x3f3f3f3f; int main() { int t,n,m,kase=0; scanf("%d",&t); while(t--){ int max_=-inf; scanf("%d%d",&n,&m); for (int i=0;i<n;i++) { int u,v; scanf("%d%d",&u,&v); if (v<=m) max_=max(max_, u); else max_=max(max_,u-(v-m)); } printf("Case #%d: %d\n",++kase,max_); } return 0; }
D - Optimal Slots(01背包)
思路:
转化下题意题意就是一个$01$背包问题
但是要求记录路径,并且要求答案相同时去最前面的方案 (这个限制我们只要从后往前枚举,遇到大小相同的方案就替代即可)
用$path[i][j]$表示到第$i$个数时容量为$j$时的选择($0$不选,$1$选),最后把路径输出就行了
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int dp[10005]; bool path[105][10005]; int c[10005],w[10005]; int main() { int N,V; while (cin>>V&&V){ cin>>N; memset(path,0,sizeof(path)); memset(dp,0,sizeof(dp)); for (int i=1;i<=N;++i){ cin>>c[i]; w[i]=c[i]; } for(int i=N;i>=1;--i){ for(int j=V;j>=c[i];--j){ if(dp[j]<=dp[j-c[i]]+w[i]){ dp[j]=dp[j-c[i]]+w[i]; path[i][j] = 1; } } } for(int i=1,j=V;i<=N&&j>0;i++){ if(path[i][j]){ printf("%d ",w[i]); j-=c[i]; } } cout<<dp[V]<<endl; } return 0; }
E - Military Class(状压DP)
思路:
由于$e$很小,我们可以考虑用二进制来表示第$i$号士兵可以匹配的士兵的状态
用$DP[i][j]$来表示第$i$号士兵,附近的士兵匹配状态为$j$时的方案数
我们假设$e=2$,并且状态$j$的二进制表示为$11010$,我们要考虑每个$1$是从哪里转移过来的
我们考虑从右到左的第二位,那么他可能从第$i-1$位士兵的$01100$与$11100$状态转移而来,所以我们可以通过考虑每个$1$是从哪里转移过来从而得到该状态的方案数
最后答案为就$dp[i][j] (j=11100)$
#include<iostream> #include<algorithm> using namespace std; const int maxn=2005; const int mod=1e9+7; int mp[maxn][maxn],dp[maxn][maxn]; int main() { int n,e,m,x,y; cin>>n>>e>>m; for(int i=1;i<=m;i++){ cin>>x>>y; mp[x][y]=1; } for(int i=0;i<=e;i++) if(!mp[i][1]) dp[1][1<<i]=1; for(int i=2;i<=n;i++){ for(int j=0;j<(1<<2*e+1);j++){ for(int k=0;k<2*e+1;k++){ if(i+e-k<1||i+e-k>n) continue; if(mp[i+e-k][i]) continue; if(j&(1<<k)){ dp[i][j]=(dp[i][j]+dp[i-1][(j^(1<<k))>>1])%mod; dp[i][j]=(dp[i][j]+dp[i-1][((j^(1<<k))>>1)|(1<<2*e)])%mod; } } } } int temp=(1<<(2*e+1))-1; for(int i=0;i<e;i++) temp-=(1<<i); cout<<dp[n][temp]%mod; }
F - Ali The Multi-billionaire(待补)
G - Are You Safe?(二维凸包)
思路:
二维凸包裸题,直接利用叉积判断点是否在凸包内即可
#include <bits/stdc++.h> using namespace std; double eps=1e-15; double pi=acos(-1); struct Point{ double x,y; Point(double x=0,double y=0):x(x),y(y){} }; typedef Point Vector; Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);} Vector operator * (Vector A,double B){return Vector(A.x*B,A.y*B);} Vector operator / (Vector A,double B){return Vector(A.x/B,A.y/B);} 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.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; } double Cross(Vector A,Vector B){ return A.x*B.y-A.y*B.x; } double Dot(Vector A,Vector B){ return A.x*B.x+A.y*B.y; } Vector Rotate(Vector A,double rad){ return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); } int tubao(Point *p,int n,Point *ch){//求凸包,返回凸包数组的长度 sort(p,p+n); int m=0; for(int i=0;i<n;i++){ while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--; ch[m++]=p[i]; } int k=m; for(int i=n-2;i>=0;i--){ while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--; ch[m++]=p[i]; } if(n>1)m--; return m; } void readp(Point &A){ scanf("%lf%lf",&A.x,&A.y); } bool onsegment(Point p,Point a1,Point a2){ if(p==a1||p==a2)return false; return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p))<0; } bool segmentcross(Point a1,Point a2,Point b1,Point b2){ if(a1==b1||a1==b2||a2==b1||a2==b2)return true; double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1), c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1); return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0; } int intubao(Point *ch,int n,Point p){//判断p点是否在凸包内 Vector A,B; int flag=0; for(int i=0;i<n;i++){ A=ch[(i+1)%n]-ch[i]; B=p-ch[i]; /*if(onsegment(p,ch[i],ch[(i+1)%n])){//这题说了点在凸包上视为在凸包外 flag=-1; break; }*/ if(Cross(A,B)>0){ flag++; } } if(flag==-1||flag==n)return 1; return 0; } int T,n,q,m; Point p1[10005],ch1[10005]; struct node{ double x,y; }g[1005]; int main(){ scanf("%d",&T); int kase=0; while(T--){ scanf("%d%d",&n,&q); for(int i=0;i<n;i++){ readp(p1[i]); } int m1=tubao(p1,n,ch1); for(int i=1;i<=q;i++){ scanf("%lf%lf",&g[i].x,&g[i].y); } printf("Case %d\n",++kase); for(int i=0;i<m1;i++){ printf("%d %d\n",(int)ch1[i].x,(int)ch1[i].y); } printf("%d %d\n",(int)ch1[0].x,(int)ch1[0].y); for(int i=1;i<=q;i++){ Point t; t.x=g[i].x; t.y=g[i].y; printf("%d %d ",(int)t.x,(int)t.y); if(intubao(ch1,m1,t))printf("is unsafe!\n"); else printf("is safe!\n"); } printf("\n"); } }
H - To Crash Or Not To Crash(签到题)
#include<iostream> #include<algorithm> using namespace std; string s[4]; int main() { for(int i=1;i<=3;i++) cin>>s[i]; int flag=0; for(int i=1;i<=3;i++){ for(int j=0;j<10;j++){ if(s[i][j]=='=') flag=1; else if(flag&&s[i][j]!='.'){ cout<<s[i][j]; flag++; break; } } if(flag==1){ cout<<"You shall pass!!!"; break; } if(flag==2) break; } return 0; }
I - Kitchen Plates(暴力)
思路:
总共就只有$2^{5}$种情况,我们全部枚举出来,再根据条件判断是否合法就行了
也可以利用拓扑排序的思想来做
#include<iostream> #include<algorithm> using namespace std; string s[6]; int a[6],vis[6]; void dfs(int k) { if(k>4){ int flag=0; for(int i=1;i<=5;i++){ int x=s[i][0]-'A'; int y=s[i][2]-'A'; int pos1,pos2; for(int j=0;j<5;j++){ if(a[j]==x) pos1=j; if(a[j]==y) pos2=j; } if(s[i][1]=='>'&&pos1>pos2) continue; if(s[i][1]=='<'&&pos1<pos2) continue; flag=1; break; } if(!flag){ for(int i=0;i<5;i++) printf("%c",'A'+a[i]); exit(0); } return; } for(int i=0;i<5;i++){ if(vis[i]==0){ a[k]=i; vis[i]=1; dfs(k+1); vis[i]=0; } } return; } int main() { for(int i=1;i<=5;i++) cin>>s[i]; dfs(0); cout<<"impossible"; return 0; }