1. 程式人生 > >[計蒜客NOIP模擬賽]2017.7.28Day1回顧反思總結

[計蒜客NOIP模擬賽]2017.7.28Day1回顧反思總結

n) 模擬 查詢 同時 結點 reg etc mes 停止

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一等都是一個問題,所以拼命努力吧~

還有就是考試技巧非常重要,多參加比賽練習賽感,減少比賽失誤!!!

[計蒜客NOIP模擬賽]2017.7.28Day1回顧反思總結