D1T1 打地鼠
题目链接
反思-
比赛得分-0
思考:
比赛时,以为T1是一道常规模拟题目,没怎么看数据范围。直接手动模拟,模拟完之后太自信也没有造数据Hack自己的程序。直接导致爆0。同时发现自己对二维前缀和的学习也只是在皮毛之上,没有深入思考与理解。
解题思路-
将图像旋转45°之后用二维前缀和维护,每次O(1)查询,时间复杂度O(N*N)。
但是目前觉得这个图像旋转45°难以理解,打算手动模拟加深理解。
标程
#include<bits/stdc++.h> using namespace std; int n,m,k; int a[4050][4050],b[4050][4050],s[4050][4050],vis[4050][4050]; int main(){ //freopen("mouse10.in","r",stdin); //freopen("mouse10.ans","w",stdout); scanf("%d%d%d",&n,&k);m=n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); int l=n+n-1,ll=2*l-1; for(int i=1;i<=ll;i++){ if(i>=n&&(i-n)%2==0){ int num=(i-n)/2+1; //if(i==9) cout<<num<<endl; for(int j=num;j<=num+n-1;j++) b[j][i+1-j]=a[j-num+1][num],vis[j][i+1-j]=1; } } for(int i=1;i<=l;i++) for(int j=1;j<=l;j++) s[i][j]=b[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1]; int ans=0; for(int i=1;i<=l;i++) for(int j=1;j<=l;j++) if(vis[i][j]){ int zsx=max(1,i-k+1),zsy=max(1,j-k+1),zxx=min(l,i+k-1),zxy=max(1,j-k+1); int ysx=max(1,i-k+1),ysy=min(l,j+k-1),yxx=min(l,i+k-1),yxy=min(l,j+k-1); ans=max(ans,s[yxx][yxy]-s[ysx-1][yxy]-s[zxx][zxy-1]+s[zsx-1][zsy-1]); } printf("%d\n",ans); }
D1T2 蒜头君的树
反思-
比赛得分-30
思考:
比赛时,以为是用数据结构维护,生硬套上了树剖模板,但是得分只有30。没有深入去思考,每个变化的边的所带来的影响。
解题思路-
乘法原理在树上的运用,我们假设结点u是结点v的父亲,其边权为w。那么这条边权会被最终答案计算多少次呢?没错就是除儿子v及其子结点的数量*儿子v及其子结点的数量。
修改的话只考虑,修改的那条边对其他结点的影响。 我们假设修改的结点是 x,那么影响的结点数量就是x及其子孙数量*其他结点数。
#include <cstdio> #include <algorithm> #include <vector> typedef long long ll; const int maxn = 1e5+5; struct node{ int v,w; }; std::vector<node>G[maxn]; int n,m,bian[maxn]; ll sum[maxn],dp[maxn]; int Read(){ int ch = 0; char c; c = getchar(); while( c<'0' || c>'9') c = getchar(); while( c>='0' && c<='9') { ch = ch*10+c-'0'; c = getchar(); } return ch; } void dfs(int x,int fa){ sum[x] = 1; int Size = G[x].size(); for(int i=0;i<Size;i++){ int v = G[x][i].v; int w = G[x][i].w; if(v==fa) continue; dfs(v,x); sum[x]+=sum[v]; dp[x]+=(dp[v]+((n-sum[v])*sum[v])*w); //关键部分 } } void AddEdge(int u,int v,int w){ node now; now.v=v; now.w=w; G[u].push_back(now); now.v=u; G[v].push_back(now); } int main(){ n = Read(); for(register int i=1;i<n;i++){ int v,w; v = Read(); w = Read(); AddEdge(i+1,v,w); bian[i+1]=w; } dfs(1,0); printf("%lld\n",dp[1]); m = Read(); for(int i=1;i<=m;i++){ int x,y; x = Read(); y = Read(); ll ans = dp[1]+sum[x]*(n-sum[x])*(y-bian[x]);//只考虑修改的边影响的结果 bian[x] = y; dp[1] = ans; printf("%lld\n",ans); } return 0; }
D1T3 蒜头君的坐骑
题目链接
反思-
比赛得分-0
思考:
比赛时,在T2耗费了两小时,这个时间没有时间思考了。所以草草写了一个我自己都看不懂的辣鸡暴力,成功爆0!
解题思路-
暴力搜索30分
用dp[n][m][k]表示牛神现在在(n,m)点,已经使用了k次技能.对于每次决策,如果不使用技能,可以转移到dp[n][m+1][k],dp[n+1][m][k],如果使用技能,则可通过dfs搜索可以转移到的每一个点,时间复杂度O(n*m*k).
不得不说这是第一次见到用DFS来求DP值,算是大开眼界了。反反复复阅读与抄写了四遍之后。看懂了,DFS和DP的含义。
#include<bits/stdc++.h> using namespace std; int n,m,t,k,h,atk; int dp[1050][1050][15],mp[1050][1050]; int cal(int x,int y,int at){ //计算怪物对你造成的伤害 h-1的原因是如果h==at那么应该是秒杀不造成伤害 return (h-1)/at*mp[x][y]; } void dfs(int x,int y,int res,int st,int at,int dam){ if(st) dam+=cal(x,y,at); //在使用技能时,应计算是否受到伤害 //更新最终停止在x,y点的使用技能情况下的最小伤害值 太妙了 if(st==k) {dp[x][y][res+1]=min(dp[x][y][res+1],dam);return;} if(x+1<=n) dfs(x+1,y,res,st+1,at+mp[x+1][y],dam);//往下走的情况 if(y+1<=m) dfs(x,y+1,res,st+1,at+mp[x][y+1],dam);//往右走的情况 } int main(){ scanf("%d%d%d%d%d%d",&n,&m,&t,&k,&h,&atk); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int l=0;l<=t;l++) dp[i][j][l]=999999999; //在起点没有使用过技能所以为0 dp[1][1][0]=0; for(int l=0;l<=t;l++) for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ //先考虑如果不使用技能,往下走的情况的最小伤害 if(i+1<=n) dp[i+1][j][l]=min(dp[i+1][j][l],dp[i][j][l]+cal(i+1,j,atk)); //不使用技能,往右走的最小伤害 if(j+1<=m) dp[i][j+1][l]=min(dp[i][j+1][l],dp[i][j][l]+cal(i,j+1,atk)); //如果技能还没使用完 那么就考虑一下在此点使用技能之后的情况 if(l!=t) dfs(i,j,l,0,atk,dp[i][j][l]); } cout<<dp[n][m][t]<<endl; }
最终总结反思
技不如人就是事实,没有天赋还天天颓。再这样下去,NOIP一等都是一个问题,所以拼命努力吧~
还有就是考试技巧非常重要,多参加比赛练习赛感,减少比赛失误!!!