Codeforces Round #612 (Div. 2)(C(四維dp),D(dfs 構造子樹序列))

Codeforces Round #612 (Div. 2)

C. Garland

題意:大概就是給你一個n排列,有些位置被拿走了,變成了零,你現在需要任意放回去,新的排列產生一個權值:

這個位置和前一個位置的數字的奇偶性不一樣 就會產生一個權值。

現問你如何重新分配0位置使得權值最小。

dp[i][j][k][z] i位置,奇偶性爲j 時剩餘奇數個數爲k,剩餘偶數個數爲z時的 最小權值。

賽時想到了這個狀態方程,無奈轉移方程一直寫不出,反應了我的dp水平還是很差。。賽後冷靜下來,仔細想了想,嗯做出來了。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
const int N=1e2+10,inf=0x3f3f3f3f;
int a[N],n,vis[N],s1,s2;
int dp[N][2][N][N];
int main()
{
    cin>>n;
    vector<int>G;
    rep(i,1,n) {
        scanf("%d",&a[i]);
        vis[a[i]]=1;
    }

    rep(i,1,n) {
        if(vis[i]) continue;
        if(i%2) s1++;
        else s2++;
    }
    memset(dp,inf,sizeof(dp));
    dp[0][0][s1][s2]=0;
    dp[0][1][s1][s2]=0;
    int id=1;
    if(a[1]==0){
        dp[1][0][s1][s2-1]=0;
        dp[1][1][s1-1][s2]=0;
    }
    else {
        if(a[1]%2==0)
        dp[1][0][s1][s2]=0;
        else
        dp[1][1][s1][s2]=0;
    }

    
    rep(i,2,n){
        if(a[i]==0){
            rep(jj,0,1)
            rep(j,0,1)
            rep(k,0,s1)
            rep(z,0,s2){
                if(j!=jj) {
                    if(j==0)
                    {
                        if(z-1>=0)
                        dp[i][j][k][z-1]=min(dp[i][j][k][z-1],dp[i-1][jj][k][z]+1);
                    }
                    else {
                        if(k-1>=0)
                        dp[i][j][k-1][z]=min(dp[i][j][k-1][z],dp[i-1][jj][k][z]+1);
                    }
                }
                else {
                    if(j==0) {
                        if(z-1>=0)
                        dp[i][j][k][z-1]=min(dp[i][j][k][z-1],dp[i-1][jj][k][z]);
                    }
                    else {
                        if(k-1>=0)
                        dp[i][j][k-1][z]=min(dp[i][j][k-1][z],dp[i-1][jj][k][z]);
                    }
                }
            }
        }
        else{
            rep(jj,0,1)
            rep(k,0,s1)
            rep(z,0,s2){
                int d=a[i]%2;
                if(d!=jj){
                        dp[i][d][k][z]=min(dp[i][d][k][z],dp[i-1][jj][k][z]+1);
                }
                else{
                    dp[i][d][k][z]=min(dp[i][d][k][z],dp[i-1][jj][k][z]);
                }
            }
        }
    }
    printf("%d\n",min(dp[n][1][0][0],dp[n][0][0][0]));
}

D. Numbers on Tree

題意:給你一個樹,每個節點有個權值,然後每個節點有個c[i],代表着i這個子樹下有c[i]個點的值比i節點的權值小。

現問你能否構造每個節點的權值出來,能輸出這些權值。

做法:參考https://www.cnblogs.com/zzqc/p/12154912.html

大概就從子樹往上走,根據c[u]將每一個u,構造一個序列,如果u有多個子樹,那麼就將第二顆子樹的序列接在第一顆子樹的後面,爲什麼?

1,這時候很顯然的子樹之間是沒有關係的,可以給一棵子樹的值整體提高一個水平,使得得到的值也是相異的,最簡單的是加上上一棵子樹的最大值(而不一定是size,假如沒有進行算不併列的排名的話)。

子樹處理完,如何處理當前節點u呢?

子樹構造一個序列出來,當前節點u的c[u]就是這個序列的第幾位。

然後一直不知道這段怎麼去弄,發現n只有2000,那我暴力將所有的數往後移不就可以了。

用vector的insert函數就不用自己寫那麼多的代碼了。

#include<bits/stdc++.h>
using namespace std;
const int N=2e3+10;
vector<int>G[N];
int n,c[N],ans[N];
vector<int> dfs(int u,int fa)
{
    vector<int>tmp;
    for(int v:G[u]){
        if(v==fa) continue;
        vector<int>t=dfs(v,u);
        tmp.insert(tmp.end(),t.begin(),t.end());
    }

    if(c[u]>tmp.size()){puts("NO");exit(0);}
    tmp.insert(tmp.begin()+c[u],u);
    return tmp;
}

int main()
{
    scanf("%d",&n);
    int root=1;
    for(int i=1;i<=n;++i){
        int fa;
        scanf("%d%d",&fa,&c[i]);
        if(fa!=0) G[fa].push_back(i),G[i].push_back(fa);
        else root=i;
    }

    vector<int>res=dfs(root,0);
    puts("YES");
    for(int i=0;i<n;++i) ans[res[i]]=i+1;
    for(int i=1;i<=n;++i) printf("%d ",ans[i]);
    puts("");
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章