zoj 3622、Magic Number
水题
先找规律生成区间[1,1<<32-1]内的所有Magic Number,计算出来只有40多个,然后就随便YY了。
void init() { int a[5] = { 1,2,5,25,125 }; ll Max = (1ll<<32)-1; for(ll tmp =1; tmp<=Max; tmp *=10) { for(int i=0; i<5; ++i){ ll t = tmp * a[i]; if(t>Max) break; g.push_back(t); } } sort(g.begin(), g.end()); }
zoj 3623、Battle Ships
完全背包
dp[i] 表示is时,能消耗防御塔的值。
状态转移方程:dp[j] = max(dp[j-t[i]] + l[i] * ( j – t[i]))
for(int i=0; i<N; ++i) for(int j=t[i]; j<=L+1; ++j) dp[j] = max(dp[j], dp[j-t[i]] + l[i]*(j-t[i]));
zoj 3624、Count Path Pair
题意:从A到D与从B到C的路径中不相交的有多少条。
这题从反面考虑:
Answer = SUM(总的情况:C(m+n,m)*C(q+m-p,q) ) - (A->D 与 B->C路径相交的情况)
然后重点分析:
(A->D 与 B->C路径相交的情况)
看下图:
发现:所有A->D 与B->C相交的情况都可以转化为A->C 与B->D的情况。
所以:Answer = ( C(m + n, n) * C(m + q - p, q) - C(m + q, q) * C(n + m - p, n)) % 100000007。
注意:计算组合C(n,m)时,由于在计算阶乘的时候用了取模(不取模会溢出),在最后计算(n*...*n-m+1)/ m!时得到将非常不一定是正确结果,所以在这一步中我们可以采用 乘法逆
转化为 (n*..n-m+1) * inv(m!, mod).这样就不会出问题了。
void gcd(ll a, ll b, ll& d, ll& x, ll& y){ if(!b){ d = a; x = 1; y = 0; } else { gcd(b, a%b, d, y, x); y -= x*(a/b); } } ll inv(ll a, ll n){ ll d, x, y; gcd(a, n, d, x, y); return d == 1 ? (x+n)%n : -1; } ll C(ll n, ll m){ ll t1 = 1, t2 = 1; for(ll i=1; i<=m; ++i){ t1 =(t1*(n-i+1)) % mod; t2 = (t2*i)% mod; } return t1*inv(t2, mod) % mod; }
zoj 3625、Geek‘s Collection
不会....
zoj 3626、Treasure Hunt I
树形DP
给定n个点的一棵树,点有点权,边有边权,要求从某点出发,再回到原点能获得的最多点权。
把点权作为价值,边权作为消耗,就是一个树形背包。
把总能量m /= 2 ,就不用考虑再回到原点的部分。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 300; int dp[maxn+10][maxn+10]; struct node { int v,t; node(int v=0, int t=0):v(v),t(t) {} }; vector<node> g[maxn]; int v[maxn]; int n, k , m; int dfs(int u, int pre) { dp[u][0] = v[u]; for(int i=0; i<g[u].size(); ++i) { node &e = g[u][i]; if(e.v==pre) continue; dfs(e.v, u); for(int j=m; j>=e.t; --j) { for(int p=j; p>=e.t; --p) { dp[u][j] = max(dp[u][j], dp[u][j-p]+dp[e.v][p-e.t]); } } } } int main() { //freopen("in.txt","r",stdin); while(~scanf("%d", &n)) { for(int i=1; i<=n; ++i) { scanf("%d", &v[i]); } int x, y, z; for(int i=0; i<=n; ++i) g[i].clear(); for(int i=1; i<n; ++i) { scanf("%d%d%d", &x, &y, &z); g[x].push_back(node(y,z)); g[y].push_back(node(x,z)); } scanf("%d%d", &k, &m); m /= 2; memset(dp, 0, sizeof dp ); dfs(k, -1); int ans = 0; for(int i=0; i<=m; ++i) ans = max(ans, dp[k][i]); printf("%d\n", ans); } return 0; }
zoj 3627、Treasure Hunt II
贪心
因为每个点的黄金只能走一次,而且有有时间、距离限制,那么一开始的时候肯定可以一起往两边跑,看到黄金就抢,直到超过距离或者时间不够或者到边界不能跑为止。这时候是双核,这样跑肯定最优。然后开始要么往左边一直跑,右边的跟着一直跑,遇到左边界就往右跑,要么就往右边一直跑,左边的跟着一直跑,遇到右边界就往左跑。如此这般就过掉了这题。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <cmath> #include <set> #include <map> using namespace std; typedef long long ll; const int maxn = 100010; int n, m, t, p; ll val[maxn]; ll sum[maxn]; ll func(ll *sum, int p) { ll ans = val[p]; for(int r=min(n, p+t); r >= p; --r) { int l = max(1, r- m); l = max(p - t, l); int res = t - max(p - l, (r - p)); int l1 = max(1, l - res); int r1 = min(n, r + res); ans = max(ans, max(sum[r]-sum[l1-1], sum[r1]-sum[l-1])); } return ans; } int main() { while(~scanf("%d%d", &n, &p)) { sum[0] = 0; for(int i=1; i<=n; ++i) { cin >> val[i]; sum[i] = sum[i-1] + val[i]; } scanf("%d%d", &m, &t); ll ans = func(sum, p); for(int i=1; i<=n/2; ++i) swap(val[i], val[n+1-i]); for(int i=1; i<=n; ++i) sum[i] = sum[i-1] + val[i]; ans = max(ans, func(sum, n+1-p)); cout<<ans<<endl; } return 0; }
zoj 3628、Treasure Hunt III
DP
不会。。。待补
zoj 3629、Treasure Hunt IV
找规律
http://blog.csdn.net/u010304217/article/details/38903983
zoj 3630、Information
Tarjan
一个有向图,求删除一个点后,是得所有联通分量中点的数量得最大值,最小。
//求图中去掉一个点,求剩余的最大的联通分量中的点的数量。 //删去某点后,最大点数 最小是多少 #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <stack> #include <iostream> using namespace std; const int maxn = 110; vector<int> G[maxn]; int dfn[maxn], low[maxn], belong[maxn], dfs_clock, scc_cnt; stack<int> S; int n, m, ans, del, cnt, ct; void dfs(int u){ dfn[u] = low[u] = ++dfs_clock; S.push(u); for(int i=0; i<G[u].size(); ++i){ int v = G[u][i]; if(v == del) continue; if(!dfn[v]){ dfs(v); low[u] = min(low[u], low[v]); }else if(!belong[v]){ low[u] = min(low[u], dfn[v]); } } if(low[u] == dfn[u]){ scc_cnt++; int s = 0; for(;;){ int x = S.top(); S.pop(); belong[x] = scc_cnt; s++; if(x == u) break; } if(s>1&&s>ct) ct = s; } } void find_scc(int n){ dfs_clock = scc_cnt = 0; memset(belong, 0, sizeof belong ); memset(dfn, 0, sizeof dfn ); cnt = 0; for(int i=0; i<n; ++i){ ct = 0; if(!dfn[i]&& i!=del) dfs(i); cnt = max(cnt, ct); } } int main() { while(~scanf("%d%d", &n, &m)) { for(int i=0; i<=n; ++i) G[i].clear(); int x, y; for(int i=0; i<m; ++i){ scanf("%d%d", &x, &y); G[x].push_back(y); } ans = 1e8; while(!S.empty()) S.pop(); for(del = 0; del<n; ++del) { find_scc(n); ans = min(ans, cnt); } printf("%d\n", ans); } return 0; }
zoj 3631、Watashi‘s BG
搜索+剪枝
#include <cstdio> #include <algorithm> using namespace std; int n, m; int a[35]; int s[360]; int Max; void dfs(int x, int y) //dfs(i, money) { if(y>m) return ; Max = max(y, Max); if(y+s[x]<=Max) return ; //剪纸 if(y+a[x]<=m) dfs(x-1, y+a[x]); dfs(x-1, y); } int main() { int i; while(scanf("%d%d",&n,&m)!=EOF) { for(i=1;i<=n;i++)scanf("%d",a+i); sort(a+1,a+n+1); s[0]=0; Max=0; for(i=1;i<=n;i++)s[i]=s[i-1]+a[i]; dfs(n,0); printf("%d\n",Max); } return 0; }
zoj 3632、Watermelon Full of Water
DP+线段树优化
不会。。。待补