题解:
第一题: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 ", cnt); }
第二题:树上背包,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 ", dp[i][j], dp[i][j]); printf(" %d ", i); }*/ printf("%d ", zz); }
第三题:状压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 ",dp[c][(1 << r) - 1]); }
第四题:背包,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 ", tot); for(int i = 1; i <= tot; i++)printf("%d ", tp[i]); }