The 2017 ACM-ICPC Asia Beijing Regional (賽後整理)
PS: E,F是個大水題,但是因爲隊伍英語水平不高(菜是原罪),E題隊友40分鐘才A,我去讀F題,發現是個水題,20分鐘後又把F題A了,之後 J 題讀錯題意(successive 讀成“成功的”,導致理解錯題意,爆搜搞答案,當然是一直tle或wa到底)G題讀完發現是個sb題,bfs+線段多邊形規範相交就行,wa到底,比賽結束後發現我纔是sb,G題座標都沒轉換正確,線段多邊形規範相交還有一種情況沒判斷。H題讀完題意,發現不會。
比賽結束就A了2道,…好菜啊
不過這次的幾何G題和 區間dp J 題不錯。
E - Cats and Fish
思路:這道題用優先隊列或者set模擬一下即可,太水就鴿了吧(本來很早就要寫這題,不過沒時間…拖到現在也不想搞了)
F - Secret Poems
思路:斜着一遍把原字符串還原,然後蛇形填數形成新的grid即可。
水題
代碼:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e2+10;
char g[N][N];
char a[N][N];
string s;
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1; i<=n; ++i) scanf("%s",g[i]+1);
s="";
for(int i=1; i<=n; ++i)
{
if(i&1)//向上
{
for(int x=i,y=1; x>=1; x--,y++)
s+=g[x][y];
}
else
{
for(int x=1,y=i; y>=1; x++,y--)
s+=g[x][y];
}
}
for(int i=2; i<=n; ++i)
{
if((n+i-1)&1)//向上
{
for(int x=n,y=i; y<=n; y++,x--)
s+=g[x][y];
}
else
{
for(int x=i,y=n; x<=n; x++,y--)
s+=g[x][y];
}
}
mset(a,0);
int k=0;
a[1][1]=s[k++];
int x=1,y=1;
int mx=n*n;
while(k < mx)
{
while(k < mx&&y < n&&a[x][y+1]==0)//向前走y++;
{
y++;
a[x][y]=s[k++];
}
while(k<mx && x<n &&a[x+1][y]==0)//向下走 x++;
{
x++;
a[x][y]=s[k++];
}
while(k<mx && y > 1&&a[x][y-1]==0)//向左走 y--;
{
y--;
a[x][y]=s[k++];
}
while(k<mx &&x>1 && a[x-1][y] ==0)//向上走 x--
{
x--;
a[x][y]=s[k++];
}
}
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
printf("%c",a[i][j]);
puts("");
}
}
return 0;
}
G - Liaoning Ship’s Voyage
題意:給出一個n*n的格子,和一個三角形,開始自己在左下角(0,0),現要去(n-1,n-1)座標。每次可以走周圍的8個方向,不能走’#’,每走一次的路線是直線,要求走的路線(線段)不能穿過三角形,但可以在三角形邊上走。問走的最小步數。
思路:bfs+線段多邊形規範相交吧,在輸入座標方面注意x軸是輸入的列,y軸是輸入的行,需要轉化下。判斷線段多邊形不規範相交,首先線段與多邊形的所有邊都不規範相交,其次兩個點都不在多邊形內,但可能出現兩個點在多邊形邊上,但中間的線段穿過多邊形,這時可以在多邊形上取幾百個點判斷下即可。
代碼:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
double xx[10],yy[10];
double const eps=1e-8;
char gg[25][25],g[25][25];
int s[25][25];
int n;
int dir[8][2]= {0,1,0,-1,1,0,-1,0,1,1,1,-1,-1,1,-1,-1};
struct node
{
int x,y,s;
node() {}
node(int x,int y,int s):x(x),y(y),s(s) {}
};
int sgn(double x)
{
if(abs(x)<eps) return 0;
if(x<0) return -1;
else return 1;
}
double add(double a,double b)
{
if(abs(a+b)<eps*(abs(a)+abs(b))) return 0;
return a+b;
}
struct Point
{
double x,y;
Point() {}
Point(double x,double y):x(x),y(y) {}
Point operator -(Point p)
{
return Point(add(x,-p.x),add(y,-p.y));
}
Point operator +(Point p)
{
return Point(add(x,p.x),add(y,p.y));
}
double operator ^(Point p)
{
return add(x*p.y,-y*p.x);
}
Point operator *(double d)
{
return Point(x*d,y*d);
}
double operator *(Point p)
{
return add(x*p.x,y*p.y);
}
} avg[3];
struct Line
{
Point s,e;
Line() {}
Line(Point s,Point e):s(s),e(e) {}
} line[10];
int getDirPPP(Point p,Point p1,Point p2)
{
return sgn((p1-p)^(p2-p));
}
bool onSge(Line l,Point q)
{
return ((l.s-q)^(l.e-q))==0&&((l.s-q)*(l.e-q)) <=0;
}
bool isInterSS(Line la,Line lb)//判斷規範相交
{
int d1=getDirPPP(lb.s,lb.e,la.s);
int d2=getDirPPP(lb.s,lb.e,la.e);
int d3=getDirPPP(la.s,la.e,lb.s);
int d4=getDirPPP(la.s,la.e,lb.e);
if(d1*d2<0&&d3*d4<0)
return true;
return false;
}
bool notinari(Point a)//點在三角形內返回flase//邊上返回true
{
for(int i=0; i<3; ++i)
if(getDirPPP(a,avg[i],avg[(i+1)%3])==0) return true;
if(getDirPPP(a,avg[0],avg[1]) < 0&& getDirPPP(a,avg[1],avg[2])<0 && getDirPPP(a,avg[2],avg[0])<0) return false;
if(getDirPPP(a,avg[0],avg[1]) > 0&& getDirPPP(a,avg[1],avg[2])>0 && getDirPPP(a,avg[2],avg[0])>0) return false;
return true;
}
bool judge(Line a)
{
for(int i=0; i<3; ++i)
if(isInterSS(a,line[i]))
return false;
double d=0.01,s=1;
for(int i=0; i<100; ++i)
{
Point t=a.e+(a.s-a.e)*s;
if(!notinari(t)) return false;
s-=d;
}
if(notinari(a.s)&¬inari(a.e))
return true;
else
return false;
}
int work()//下標從0開始
{
mset(s,-1);
s[0][0]=0;
queue<node> Q;
Q.push(node(0,0,0));
while(!Q.empty())
{
node o=Q.front();
Q.pop();
for(int i=0; i<8; ++i)
{
int nx=o.x+dir[i][0];
int ny=o.y+dir[i][1];
if(nx>=0&&nx<n&&ny>=0&&ny<n&&g[nx][ny]=='.'&&s[nx][ny]==-1)
{
if(judge(Line(Point(1.0*nx,1.0*ny),Point(1.0*o.x,1.0*o.y))))
{
// printf("--nx:%d,ny:%d\n",nx,ny);
Q.push(node(nx,ny,o.s+1));
s[nx][ny]=o.s+1;
}
}
}
}
return s[n-1][n-1];
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0; i<3; ++i)
{
scanf("%lf%lf",&xx[i],&yy[i]);
avg[i]=Point(xx[i],yy[i]);
}
int top=0;
for(int i=0; i<3; ++i)
for(int j=i+1; j<3; ++j) //3
line[top++]=Line(Point(xx[i],yy[i]),Point(xx[j],yy[j]));
for(int i=n-1; i >=0; --i)
scanf("%s",gg[i]);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
g[i][j]=gg[j][i];
cout<<work()<<endl;
}
return 0;
}
H - Puzzle Game
題意:給出一個n*m的矩形和一個整數p,可以選擇在矩形中選擇一個數將之變成p,要求最後的最大子矩陣和最小。
思路:不會
J - Pangu and Stones
題意:給出n個石子和石子的重量,每次可以選擇L到R個石子合併成一個,花費爲合併的石子的重量,求合併成一堆的最小花費,不能合成一堆輸出0。
思路:
這題感覺很有意思,我們用表示狀態,但是這個狀態的含義有些豐富(
當時,表示 到 的石子合併成一堆所需的最小花費。
當時,表示 到 的石子劃分成 堆的最小花費,注意這裏的劃分是還沒有合併,只是劃分。
轉移方程:
。注意這裏的表示 到 的總重量,這個狀態轉移表示的是一次合併操作,
當時, ,這裏表示的是劃分操作。
代碼:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=105;
const int inf=0x3f3f3f3f;
int a[N],sum[N];
int dp[N][N][N];
int main()
{
int n,L,R;
while(~scanf("%d%d%d",&n,&L,&R))
{
for(int i=1;i<=n;++i) scanf("%d",a+i);
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
for(int k=1; k<=R; ++k) dp[i][j][k]=inf;
for(int i=1; i<=n; ++i) sum[i]=sum[i-1]+a[i];
for(int i=1; i<=n; ++i) dp[i][i][1]=0;
for(int ls=2; ls<=n; ++ls)
{
for(int l=1; l+ls-1 <= n; ++l)
{
int r=l+ls-1;
//先處理k\in[2,R]的情況
for(int k=2; k<=R; ++k) //dp r-c>=k-1 c<=r-k+1
{
for(int c=l; c<=r-k+1; ++c) //dp[l][c][1] +dp[c+1][r][k-1]
dp[l][r][k]=min(dp[l][r][k],dp[l][c][1]+dp[c+1][r][k-1]);
}
//處理k=1的情況
for(int k=L; k<=R; ++k)
dp[l][r][1]=min(dp[l][r][1],dp[l][r][k]+sum[r]-sum[l-1]);
}
}
if(dp[1][n][1]==inf)
printf("0\n");
else
printf("%d\n",dp[1][n][1]);
}
return 0;
}