Cats and Fish
- 题意: n只猫,m条鱼,第i只猫吃一只鱼需要\(C_i\),每只猫吃完当前这条鱼立即去吃下一条,问\(T\)时刻还剩多少条鱼,有多少条鱼正在被吃
- 思路: 以为是贪心,但是wa了(不懂),要模拟,具体看注释吧
const int N = 2*1e5+10; int n,m,k; int a[N]; int v[N]; int main(){ while(scanf("%d%d%d",&m,&n,&k)==3){ int q = 0, p = 0 , t = m; // q 被猫拿走的鱼, p 被猫吃完的鱼, t 还剩多少条鱼 for(int i=1;i<=n;++i) scanf("%d",&a[i]); sort(a+1,a+1+n); // 吃的快的猫先拿 for(int i=1;i<=k;++i){ // 时间 for(int j=1;j<=n;++j){ if(t==0) break; if(i%a[j]==1 || a[j]==1){ // 余数是1或吃鱼时间为1,当前这只猫拿鱼 t--; q++; } if(i%a[j]==0){ // 整除,说明这只猫吃掉一条鱼 p++; } } } printf("%d %d\n",m-q,q-p);// m-q 没有被猫拿走的鱼, p-q 被拿走但没有被吃完的鱼 } return 0; }
Secret Poems
- 题意: 给出一个字母矩阵,原来按照对角线s型排列,让你转换成回型排列
- 思路: bfs,遇到边界拐弯.
const int N = 120; char s[N*N]; char a[N][N],b[N][N]; int vis[N][N]; int n; void dfs(int x,int y,int dep){ // cout << x << ' ' << y << endl; if(dep>n*n) return; s[dep] = a[x][y]; vis[x][y] = 1; if(x==1){ if(y-1>0 && !vis[x+1][y-1]){ dfs(x+1,y-1,dep+1); }else if(y+1<=n && !vis[x][y+1]){ dfs(x,y+1,dep+1); }else{ dfs(x+1,y,dep+1); } }else if(x==n){ if(y+1<=n && !vis[x-1][y+1]){ dfs(x-1,y+1,dep+1); }else{ dfs(x,y+1,dep+1); } }else if(y==1){ if(x-1>0 && !vis[x-1][y+1]){ dfs(x-1,y+1,dep+1); }else{ dfs(x+1,y,dep+1); } }else if(y==n){ if(x+1<=n && !vis[x+1][y-1]){ dfs(x+1,y-1,dep+1); }else{ dfs(x+1,y,dep+1); } }else{ if(x-1>0 && y+1<=n && !vis[x-1][y+1]){ dfs(x-1,y+1,dep+1); }else if(x+1<=n && y-1>0 && !vis[x+1][y-1]){ dfs(x+1,y-1,dep+1); } } } int d[][2] ={{0,1},{1,0},{0,-1},{-1,0}}; void dfs2(int x,int y,int dep,int dir){ b[x][y] = s[dep]; vis[x][y] = 1; int tx = d[dir][0]+x, ty = d[dir][1]+y; if(tx<=n && tx>0 && ty<=n && ty>0 && !vis[tx][ty]) dfs2(tx,ty,dep+1,dir); else{ dir = (dir+1)%4; tx = d[dir][0]+x, ty = d[dir][1]+y; if(tx<=n && tx>0 && ty<=n && ty>0 && !vis[tx][ty]) dfs2(tx,ty,dep+1,dir); } } int main(){ while(scanf("%d",&n)==1){ memset(vis,0,sizeof vis); for(int i=1;i<=n;++i){ scanf("%s",a[i]+1); } dfs(1,1,1); memset(vis,0,sizeof vis); dfs2(1,1,1,0); for(int i=1;i<=n;++i){ for(int j=1;j<=n;++j){ putchar(b[i][j]); }putchar('\n'); } } return 0; }
Pangu and Stones
- 题意: 合并石子,每次可以合并长度\(l\sim r\)的区间,问合并成一堆的最小值
- 思路:
优先队列模拟直接WA掉, dp[i][j][k] 表示从i到j合并成k堆的最小值
dp[l][r][p] = min dp[l][k][p-1] + dp[k+1][j][1] (i<=k<j,2<=p<=r)
dp[l][r][1] = min dp[l][r][p] + sum[l] - sum[r] (l<=p<=r)
先考虑合并成一堆,只能通过合并\(l\sim r\)个堆.
合并成多堆时,选择后面一个堆,合并到前面p-1个堆,实质是转移p堆的值没有实际进行合并
int a[N],sum[N]; int dp[N][N][N]; int n,l,r; int main(){ while(scanf("%d%d%d",&n,&l,&r)==3){ memset(dp,0x3f,sizeof dp); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); sum[i] = sum[i-1]+a[i]; } for(int i=1;i<=n;++i){ for(int j=i;j<=n;++j){ dp[i][j][j-i+1] = 0; } } for(int len = 2;len<=n;++len){ for(int i=1;i+len-1<=n;++i){ int j = i+len-1; for(int p=2;p<=r;++p){ for(int k=i;k<j;++k){ if(k-i+1<p-1) continue; // k到i 不够p-1个数 肯定合不成p-1个堆 dp[i][j][p] = min(dp[i][j][p],dp[i][k][p-1]+dp[k+1][j][1]); } } for(int p=l;p<=r;++p){ dp[i][j][1] = min(dp[i][j][1],dp[i][j][p]+sum[j]-sum[i-1]); } } } if(dp[1][n][1]==0x3f3f3f3f) dp[1][n][1] = 0; // 没有更新到最终的区间 无解 printf("%d\n",dp[1][n][1]); } return 0; }
Liaoning Ship’s Voyage
- 题意: 八连通,经典bfs,但加入了一个不可经过的三角形区域,问从左下角到右上角的最短路
- 思路: 一开始写预处理所有三角形内部的点,设置其不可经过,但这题值域并不是整数范围,需要在bfs到下一个点时判断路径是否经过了三角形(离散枚举100个点)
const int N = 40; const double eps = 1e-7; const int dir[][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,-1},{1,1},{1,-1},{-1,1}}; struct point { double x,y; point(double x=0.0,double y=0.0):x(x),y(y){} double det(const point oth)const{ return x*oth.y - y*oth.x; } point operator - (const point oth)const{ return point(x-oth.x,y-oth.y); } }p[3]; struct node{ int x,y,dep; node(int x=0,int y=0,int dep=0):x(x),y(y),dep(dep){} }cur,nxt; char ma[N][N]; int vis[N][N]; int n; int dcmp(double x){ if(fabs(x)<eps) return 0; if(x>0) return 1; return -1; } double cross(point a,point b){return a.x*b.y - a.y*b.x;} bool check(point pt){ // point p1 = p[0]- pt, p2 = p[1] - pt , p3 = p[2]-pt; point p1 = pt - p[0], p2 = pt - p[1], p3 = pt - p[2]; int r1 = dcmp(cross(p1,p2)), r2 = dcmp(cross(p2,p3)), r3 = dcmp(cross(p3,p1)); return r1+r2+r3==3||r1+r2+r3==-3; } void bfs(){ queue<node> que; que.push(node(0,0,0)); vis[0][0] = 1; double tmpx,tmpy,dx,dy; int sign; while(!que.empty()){ cur = que.front(); que.pop(); if(cur.x == n-1 && cur.y == n-1){ printf("%d\n",cur.dep); return ; } nxt.dep = cur.dep+1; for(int i=0;i<8;++i){ nxt.x = cur.x + dir[i][0]; nxt.y = cur.y + dir[i][1]; if(nxt.x >=0 && nxt.x <n && nxt.y>=0 && nxt.y <n && !vis[nxt.x][nxt.y] && ma[nxt.x][nxt.y]=='.'){ tmpx =cur.x; tmpy = cur.y; // 经过点 dx = 1.0*dir[i][0]/100; dy = 1.0*dir[i][1]/100; // 步长 sign = 0; for(int j=0;j<=100;++j){ if(check(point(tmpy,tmpx))){// 题目的x(行),y(列)和存储的x(列),y(行)是相反的 sign = 1;break; } tmpx += dx; tmpy += dy; } if(sign==0){ // 没经过三角形,才可以走这个方向 vis[nxt.x][nxt.y] = 1; que.push(nxt); } } } } printf("-1\n"); return ; } int main(){ while(scanf("%d",&n)==1){ memset(vis,0,sizeof vis); for(int i=0;i<3;++i){ scanf("%lf%lf",&p[i].x,&p[i].y); } for(int i=n-1;i>=0;--i){ scanf("%s",ma[i]); } bfs(); } return 0; }
l1,l2,l3顺序排列,点在凸多边形内必定有相同的转向(l2在l1左,l3在l2左,l1在l3左)可以O(n)判断在凸多边形内O(logn)太麻烦
Puzzle Game
- 题意: 可以修改矩阵中一个值为K,求最大子矩阵和最小
- 思路: 如果要修改肯定在最大子矩阵内部,枚举最大子矩阵元素进行修改,修改后的最大子矩阵等于 max(上方最大子矩阵和,下方最大子矩阵和,左方最大子矩阵和,右方最大子矩阵和,包含这个点后的最大子矩阵和 利用当前最大的和维护 )
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int N = 510; int n,m,p; int sum[N],dp[N],u[N],d[N],r[N],l[N]; int a[N][N]; void solve1(){ memset(dp,0,sizeof dp); for(int i=1;i<=n;++i){ memset(sum,0,sizeof sum); for(int j=i;j<=n;++j){ int t = -1*INF, tmp = t; for(int k=1;k<=m;++k){ sum[k] += a[j][k]; dp[k] = max(dp[k-1],0)+sum[k]; tmp = max(dp[k],tmp); } for(int k=j;k<=n;++k) u[k] = max(u[k],tmp); // 上 for(int k=1;k<=i;++k) d[k] = max(d[k],tmp); // 下 } } memset(dp,0,sizeof dp); for(int i=1;i<=m;++i){ memset(sum,0,sizeof sum); for(int j=i;j<=m;++j){ int t = -1*INF, tmp = t; for(int k=1;k<=n;++k){ sum[k] += a[k][j]; dp[k] = max(dp[k-1],0)+sum[k]; tmp = max(dp[k],tmp); } for(int k=j;k<=m;++k) l[k] = max(l[k],tmp); // 左 for(int k=1;k<=i;++k) r[k] = max(r[k],tmp); // 右 } } } int main(){ while(scanf("%d%d%d",&n,&m,&p)==3){ fill(l,l+400,-1*INF); fill(d,d+400,-1*INF); fill(u,u+400,-1*INF); fill(r,r+400,-1*INF); for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j) scanf("%d",&a[i][j]); } solve1(); int ans1= u[n]; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ if(a[i][j]<=p) continue; int ans = -1*INF; ans = max(u[i-1],d[i+1]); ans = max(ans,max(l[j-1],r[j+1])); ans1 = min(ans1,max(u[n]-a[i][j]+p,ans)); // 要计算整个矩阵修改后的和 } } printf("%d\n",ans1); } }