SCOI2009题解

来源:互联网 发布:如何查看淘宝店铺号 编辑:程序博客网 时间:2024/05/17 21:40

总的来说,SCOI2009的数学比较浓,而代码相对来说都比较短,注重思维过程。

第一试:

1生日快乐

看到题目感觉无法下手,但是由于题目的限制面积必须相同且只能平行边界切注意到n的范围非常小,所以直接枚举即可

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const double inf = 1e100;const double eps = 1e-12;int x,y,n;void init(){freopen("cake.in","r",stdin);freopen("cake.out","w",stdout);}void readdata(){scanf("%d%d%d",&x,&y,&n);}double f(double x,double y,int k){if(x < y)swap(x,y);if(k == 1)return x / y;double ans = inf;for(int i = 1;i <= (k >> 1);i++){ans = min(ans,max(f(i / (double)k * x,y,i),f((k - i) / (double)k * x,y,k - i)));ans = min(ans,max(f(x,i / (double)k * y,i),f(x,(k - i) / (double)k * y,k - i)));if(ans - 1 < eps)return ans;}return ans;}void solve(){printf("%.6lf",f(x,y,n));}int main(){init();readdata();solve();return 0;}


2、windy数

比较简单的一道数位DP,需要注意好前导0的处理。

#include<cstdio>#include<cstring>using namespace std;const int maxn = 20;int dp[maxn][maxn];int digit[maxn];int A,B;void init(){freopen("windy.in","r",stdin);freopen("windy.out","w",stdout);}int abs(int x){return x >= 0 ? x : -x;}void readdata(){scanf("%d%d",&A,&B);}int dfs(int pos,int last,bool zero,bool inf){if(pos == -1)return 1;if(!inf && dp[pos][last] != -1 && !zero)return dp[pos][last];int end = inf ? digit[pos] : 9;int ans = 0;for(int i = 0;i <= end;i++){if(zero)ans += dfs(pos - 1,i,zero && i == 0,inf && i == end);else if(abs(i - last) >= 2)ans += dfs(pos - 1,i,false,inf && i == end);}if(!inf && !zero)dp[pos][last] = ans;return ans;}int calc(int x){if(x == 0)return 1;int pos = 0;while(x){digit[pos++] = x % 10;x /= 10;}return dfs(pos - 1,0,1,1);}void solve(){memset(dp,-1,sizeof(dp));printf("%d\n",calc(B) - calc(A - 1));}int main(){init();readdata();solve();return 0;}

3、游戏

原问题易转化成:

求几个长度和为n的置换的所有长度最小公倍数有多少种可能

由于1并不影响最小公倍数的可能数 -> 几个长度和小于等于n的置换的所有长度最小公倍数有多少种可能

由唯一分解定理可知,每个大于1的自然数均可写为质数的积 -> 几个小于等于n的只含一个质因数的正整数的最小公倍数有多少种可能

这样就可以使用DP来求解本题:

我们定义f[i][j]表示前i个质数,和为j的可能数,则:f[i][j] = ∑f[i-1][j-k]

#include<cstdio>#include<cstring>using namespace std;const int maxn = 300;const int maxm = 1000 + 10;const int maxprime = 100000;int p[maxn];long long f[maxn][maxm];bool flag[maxprime];int n,cnt;void init(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);}void readdata(){scanf("%d",&n);}void get_prime(){memset(p,0,sizeof(p));memset(flag,false,sizeof(flag));cnt = 0;for(int i = 2;i <= n;i++){if(!flag[i])p[++cnt] = i;for(int j = 1;j <= cnt && p[j] * i <= n;j++){flag[i*p[j]] = true;if(i % p[j] == 0)break;}}}void solve(){memset(f,0,sizeof(f));get_prime();for(int i = 0;i <= n;i++)f[0][i] = 1;for(int i = 1;i <= cnt;i++){for(int j = 0;j <= n;j++){f[i][j] = f[i-1][j];for(int k = p[i];k <= j;k *= p[i]){f[i][j] += f[i-1][j-k];}}}printf("%lld",f[cnt][n]);}int main(){init();readdata();solve();return 0;}

第二试:

1、最大距离

开始没理解“格子中心的欧几里徳距离”,后来发现就是一个坐标的距离。

同样数据范围非常小,我们对相邻的格子之间连边,边权为这两个格子的障碍格子的个数为0,1,2

这样我们以每个点为起点做spfa,若dis[i][j]<=t * 2,说明可达,更新答案就可以了。

#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int dx[4] = {-1,1,0,0};const int dy[4] = {0,0,-1,1};const int maxn = 50;const int maxque = 100000;struct pnode{int x,y;int len;pnode *next;}*first[maxn][maxn];int dis[maxn][maxn],w[maxn][maxn];intque[maxque][2];bool flag[maxn][maxn];int n,m,t;void init(){freopen("maxlength.in","r",stdin);freopen("maxlength.out","w",stdout);}void readdata(){scanf("%d%d%d",&n,&m,&t);for(int i = 1;i <= n;i++){getchar();for(int j = 1;j <= m;j++){w[i][j] = getchar() - '0';}}}void insert(int a,int b,int x,int y){pnode *p = new pnode;p -> x = x;p -> y = y;p -> len = w[a][b] + w[x][y];p -> next = first[a][b];first[a][b] = p;}void build_map(){for(int i = 1;i <= n;i++){for(int j = 1;j <= m;j++){for(int k = 0;k < 4;k++){int nx = i + dx[k],ny = j + dy[k];if(nx < 1 || nx > n || ny < 1 || ny > m)continue;insert(i,j,nx,ny);}}}}void spfa(int a,int b){memset(dis,0x3f,sizeof(dis));memset(flag,false,sizeof(flag));dis[a][b] = 0;flag[a][b] = true;int l = 0,r = 0;que[r][0] = a;que[r++][1] = b;while(l < r){int x = que[l][0],y = que[l++][1];flag[x][y] = false;for(pnode *p = first[x][y];p != NULL;p = p -> next){if(dis[x][y] + p -> len < dis[p->x][p->y]){dis[p->x][p->y] = dis[x][y] + p -> len;if(!flag[p->x][p->y]){flag[p->x][p->y] = true;que[r][0] = p -> x;que[r++][1] = p -> y;}}}}}double dist(int a,int b,int x,int y){return sqrt((a - x) * (a - x) + (b - y) * (b - y));}void solve(){build_map();double ans = 0.0;for(int i = 1;i <= n;i++){for(int j = 1;j <= m;j++){if(w[i][j])continue;spfa(i,j);for(int x = 1;x <= n;x++)for(int y = 1;y <= m;y++)if(dis[x][y] <= (t << 1))ans = max(ans,dist(i,j,x,y));}}printf("%.6lf\n",ans);}int main(){init();readdata();solve();return 0;}

2、粉刷匠

比较水的一道DP

sum[i][j][0]和sum[i][j][1]表示i行前j个格子有多少个0或1

g[i][j][k]表示i行前j个格子刷k次的最大正确涂色

f[i][j]表示前i行刷j次的最大正确涂色

则f[i][j] = max(f[i][j],f[i-1][k] + g[i][m][j-k])

g[i][j][k] = max(g[i][j][k],g[i][l][k-1] + max(sum[i][j][0] - sum[i][l][0],sum[i][j][1] - sum[i][l][1]))

即可。

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 50 + 10;const int maxt = 2500 + 10;int thi[maxt],tmp[maxn],sum[maxn][maxn][2];int g[maxn][maxn][maxt],f[maxn][maxt];int n,m,t;void init(){freopen("paint.in","r",stdin);freopen("paint.out","w",stdout);}void readdata(){memset(g,0,sizeof(g));scanf("%d%d%d",&n,&m,&t);for(int i = 1;i <= n;i++){getchar();for(int j = 1;j <= m;j++){tmp[j] = getchar() - '0';if(tmp[j] == 0){sum[i][j][0] = sum[i][j-1][0] + 1;sum[i][j][1] = sum[i][j-1][1];}else{sum[i][j][1] = sum[i][j-1][1] + 1;sum[i][j][0] = sum[i][j-1][0];}}}}void solve(){for(int i = 1;i <= n;i++){for(int k = 1;k <= m;k++)for(int j = 1;j <= m;j++)for(int l = 0;l < j;l++)g[i][j][k] = max(g[i][j][k],g[i][l][k-1] + max(sum[i][j][0] - sum[i][l][0],sum[i][j][1] - sum[i][l][1]));}memset(f,0,sizeof(f));for(int i = 1;i <= n;i++)for(int j = 0;j <= t;j++)for(int k = 0;k <= j;k++)f[i][j] = max(f[i][j],f[i-1][k] + g[i][m][j-k]);printf("%d\n",f[n][t]);}int main(){init();readdata();solve();return 0;}

3、迷路

很经典的矩阵乘法,由于权值最大为9,所以将每个点拆为[9i,9i + 9]这9个点,然后在这九个点的相邻两个点都连一条边若i -> j有条权值为k的边,则连边9i + k -> 9j

然后再使用矩阵快速幂求出t时刻从1 -> n的方案数就可以了。

#include<cstdio>#include<cstring>using namespace std;const int mo = 2009;const int maxT = 9;const int maxn = 100;struct Matrix{int v[maxn][maxn];int x,y;Matrix(){memset(v,0,sizeof(v));x = y = 0;}}map;int n,t;int tmp[maxn][maxn];void init(){freopen("road.in","r",stdin);freopen("road.out","w",stdout);}Matrix mtMul(Matrix A,Matrix B){if(!A.x || !A.y)return B;Matrix C;C.x = A.x;C.y = B.y;for(int i = 1;i <= A.x;i++){for(int j = 1;j <= B.y;j++){for(int k = 1;k <= A.y;k++){C.v[i][j] = (A.v[i][k] * B.v[k][j] + C.v[i][j]) % mo;}}}return C;}void readdata(){scanf("%d%d",&n,&t);for(int i = 1;i <= n;i++){getchar();for(int j = 1;j <= n;j++)tmp[i][j] = getchar() - '0';}}void build_map(){for(int i = 1;i <= n;i++){for(int k = 1;k < maxT;k++)map.v[(i - 1) * maxT + k][(i - 1) * maxT + k + 1] = 1;for(int j = 1;j <= n;j++){if(tmp[i][j])map.v[(i - 1) * maxT + tmp[i][j]][(j - 1) * maxT + 1] = 1;}}n *= maxT;map.x = n;map.y = n;}Matrix mtPow(Matrix A,int k){if(k == 1)return A;Matrix tmp;while(k){if(k & 1)tmp = mtMul(tmp,A);    k >>= 1;A = mtMul(A,A);}return tmp;}void solve(){build_map();Matrix ans = mtPow(map,t);printf("%d\n",ans.v[1][n - maxT + 1]);}int main(){init();readdata();solve();return 0;}



原创粉丝点击