1. 程式人生 > >暑假第十四測

暑假第十四測

題解 ret com gre esp mage truct algo dep

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

題解:

第一題:nlogn LIS

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int M = 100005;
int a[M], f[M];
int main(){
    freopen("lis.in","r",stdin);
    freopen("lis.out","w",stdout);
    int n, cnt = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        
if(a[i] > f[cnt])f[++cnt] = a[i]; else { int pos = lower_bound(f+1, f+1+cnt, a[i]) - f; f[pos] = a[i]; } } printf("%d\n", cnt); }
View Code

第二題:樹上背包,dp[u][i]表示以u為子樹連續大小為i最少砍多少刀;答案就是max(dp[u][k] -1) 減去和父親連的一條邊

dp[u][k] = min( dp[u][k], dp[u][p] + dp[v][k - p] - 1) (我們一個一個子樹考慮,多一棵樹就可以少砍一條邊)

dp[u][1] = degree[u];

技術分享圖片
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int M = 155, inf = 1e8;
int siz[M], n, k, h[M], dp[M][M], du[M], tot, zz = inf;
struct edge{int v, nxt;}G[M << 1];
void add(int u, int v){G[++tot].v = v; G[tot].nxt = h[u]; h[u] = tot;}

void dfs(int u, int fa){ //dp[u][0] = 0; if(u != 1)du[u]--; dp[u][1] = du[u]; siz[u] = 1; int child = 0; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == fa)continue; dfs(v, u); child++; siz[u] += siz[v]; } for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == fa)continue; int s1 = min(siz[u], k), s2; for(int p = s1; p > 1; p--){ s2 = min(p - 1, siz[v]); for(int z = 1; z <= s2; z++){ if(dp[u][p - z] < inf) dp[u][p] = min(dp[u][p], dp[u][p - z] + dp[v][z] - 1); } } } if(u != 1)zz = min(zz, dp[u][k] + 1); else zz = min(zz, dp[u][k]); } int main(){ freopen("isolate.in","r",stdin); freopen("isolate.out","w",stdout); scanf("%d%d", &n, &k); int u, v; for(int i = 1; i < n; i++){ scanf("%d%d", &u, &v); add(u, v); add(v, u); du[u]++; du[v]++; } //memset(mx, 127, sizeof(mx)); memset(dp, 127, sizeof(dp)); dfs(1, 0); /*for(int i = 1; i <= n; i++){ for(int j = 1; j <= k; j++)printf("%d %d\n", dp[i][j], dp[i][j]); printf(" %d\n", i); }*/ printf("%d\n", zz); }
View Code

第三題:狀壓dp,原題

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
long long dp[12][1 << 11];
int w[1 << 11][1 << 11], r, c;

void dfs(int dep, int os, int ns){
    if(!dep) w[ns][++w[ns][0]] = os;
    else{
        if( ( (1<<(dep - 1)) & os ) == 0){
            dfs(dep - 1, os, ns + (1 << (dep - 1)));
        }
        else{
            dfs(dep - 1, os, ns);
            if(dep >= 2 && ( os & (1 << (dep - 2)) ) )
                dfs(dep - 2, os, ns + (1 << (dep - 1)) + (1 << (dep - 2)));
        }
    }
}

void init(){
    
    for(int s1 = 0; s1 < (1 << r); s1++)
        dfs(r, s1, 0);
}


int main(){
    freopen("domino.in","r",stdin);
    freopen("domino.out","w",stdout);
    scanf("%d%d", &r, &c);
    if(r * c % 2){
        puts("0");return 0;
    }
    if(r > c)swap(r, c);
    init();
    dp[0][(1 << r) - 1] = 1;
    for(int i = 1; i <= c; i++){
        for(int s = 0; s < ( 1 << r ); s++){
            for(int k = 1; k <= w[s][0]; k++)
                dp[i][s] += dp[i - 1][w[s][k]];
        }
    }
    printf("%I64d\n",dp[c][(1 << r) - 1]);
}
View Code

第四題:背包,dp[p][s]表示選擇了總共p元,是否可以湊出s元;

dp[p][s] |= dp[p - c[i]][s - c[i]] | dp[p - c[i]][s]; 最後看dp[k][i] ?= 1;

我覺得我應該重學背包

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int M = 505;
bool f[M][M];
int c[M], tp[M], tong[M], tot;



int main(){
    freopen("coin.in","r",stdin);
    freopen("coin.out","w",stdout);
    int n, k, sum = 0;
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++)scanf("%d", &c[i]);    
    f[0][0]=1;
    for(int i = 1; i <= n; i++)
        for(int p = k; p >= c[i]; p--)
            for(int s = k; s >= 0; s--){
                f[p][s] |= f[p - c[i]][s];
                if(s >= c[i])f[p][s] |= f[p - c[i]][s - c[i]];
            }
    for(int i = 0; i <= k; i++)
        if(f[k][i])tp[++tot] = i;
    printf("%d\n", tot);
    for(int i = 1; i <= tot; i++)printf("%d ", tp[i]);
}
View Code

暑假第十四測