现在的位置: 首页 > 综合 > 正文

ZOJ Monthly, July 2012

2018年12月24日 ⁄ 综合 ⁄ 共 4689字 ⁄ 字号 评论关闭

zoj 3622Magic 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 3623Battle 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 3624Count Path Pair  

题意:AD与从BC的路径中不相交的有多少条。

这题从反面考虑:

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 3625Geek's Collection  

不会....

 

 

zoj 3626Treasure 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 3627Treasure 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 3628Treasure Hunt III

DP

不会。。。待补

 

 

zoj 3629Treasure Hunt IV

找规律

http://blog.csdn.net/u010304217/article/details/38903983

 

 

zoj 3630Information 

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 3631Watashi'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 3632Watermelon Full of Water  

DP+线段树优化

不会。。。待补

 

 

抱歉!评论已关闭.