JSOI2019招待

问题 A: 招待

时间限制: 1 Sec  内存限制: 128 MB  Special Judge

题目描述

请了两位奆老来为自己种树,小X也稍稍有些不好意思了,于是他准备了一些零食和饮料来招待奆老们。
然而,小X有强迫症,他希望自己和好基友们所有的零食和饮料的质量都要完全相同。
由于小X是一个奆老,所以他看不起普通商店里卖的电子秤,他决定自己做一个。
他的称重工具是一架由金子制成的天平,这架天平的精度非常高,可以达到纳克的标准,1g=109ng,小X会把物品放在天平的右侧,然后在天平的左侧和右侧都放上一些砝码,直至天平平衡。该天平的砝码是用钻石制成的,每个砝码的质量依次为1ng、3ng、9ng、27ng、81ng……,每个砝码的质量都是3的幂次(如3的6次幂表示为3^6=729),且各不相同。
由于小X是一个奆老,他有对各个物品未卜先知的能力,他会告诉你他的物品的质量,希望你给他一个方案,使得天平的两侧平衡。

输入

输入数据仅有一行包含一个正整数W,表示小X给出的物品的质量,重量单位是纳克(ng)

输出

输出数据共有两行,分别输出左右两端各个砝码及物体的质量,同一行砝码重量必须从小到大排序后按次序输出,第二行的第一个数必须先输出物体的质量,然后才是各个砝码的重量。相邻两个数之间必须严格用一个空格隔开。
注意:输入数据保证一定有解!如有多组解,输出任意一组即可!

 

样例输入

复制样例数据

67

样例输出

1 3 9 81
67 27

提示

小X给出的物品的质量为67pg,你可以在天平的左边放上4个砝码,重量依次为1,3,9,81总重量94ng,而右边放一个砝码质量为27ng,加上物体的重量67ng,恰好也是94ng,满足题目要求,此时天平的左右两端平衡。

对于20%的数据,W<=100
对于另外20%的数据,W<=10000,最多只用到5个砝码
对于另外20%的数据,W<=1000000,所有砝码都放在左边
对于另外20%的数据,W<=1000000
对于100%的数据,W<=1e15。

 

一开始想着dfs爆搜,然鹅想尽办法优化也只是能够处理1e13的数据、

知道是构造题后简单推一下就出来了,只要考虑n的三进制位置的数是怎么凑出来的就行了

 

/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
int vis[50];
int digt[35];
int main(){
    ll n;
    scanf("%lld",&n);
    vector<ll> L,R;
    R.push_back(n);
    int cnt = 0;
    while(n){
        digt[++cnt] = n%3;
        n /= 3;
    }
    //for(int i=1;i<=cnt;i++)printf("%d",digt[i]);
    ll tmp = 1;
    for(int i=1;i<=cnt;i++,tmp *= 3)if(digt[i]){
        if(digt[i] == 1){
            if(!vis[i]){
                L.push_back(tmp);
                vis[i] = 1;
            }else{
                L.pop_back();
                vis[i+1] = 1;
                L.push_back(tmp*3);
                R.push_back(tmp);
            }
        }else{
            if(!vis[i]){
                vis[i+1] = 1;
                L.push_back(tmp*3);
                R.push_back(tmp);
            }else{
                L.pop_back();
                vis[i+1] = 1;
                L.push_back(tmp*3);
            }
        }
    }
    for(int i=0;i<L.size();i++)printf("%lld%c",L[i],(i == L.size()-1)?'\n':' ');
    for(int i=0;i<R.size();i++)printf("%lld%c",R[i],(i == R.size()-1)?'\n':' ');
    return 0;
}

 

dfs双向爆搜、处理1e13可以在500ms左右,有个900ms左右的预处理

 

/*author:revolIA*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e7+4e6+17;
int head[maxn],Next[maxn],tot = 1;
pair<ll,ll> val[maxn];
ll Pow[35]={1LL};
ll out1[35],cnt1,ans[50];
ll out2[35],cnt2 = 1,mid = 13;
void Insert(ll x,ll y){//x ->> y
    int u = y%maxn;
    if(u<0)u += maxn;
    val[tot] = {y,x};
    Next[tot] = head[u];
    head[u] = tot++;
}
ll Exist(ll y){
    int u = y%maxn;
    if(u<0)u += maxn;
    for(int i=head[u];i;i=Next[i]){
        if(val[i].first == y)
            return val[i].second;
    }
    return -1LL;
}
int flag = 0,lmt = 1;
void dfs(int cur,ll x,ll y){
    if(cur>mid){
        if(Exist(y) == -1LL){
            Insert(x,y);
        }
        return;
    }
    dfs(cur+1,x*3+0,y);//
    dfs(cur+1,x*3+1,y+Pow[cur]);//+
    dfs(cur+1,x*3+2,y-Pow[cur]);//-
}
void Find(int cur,ll y){
    if(flag)return;
    if(cur>=lmt){
        if(Exist(y) != -1LL){
            //printf(">> %lld -> %lld\n",y,Exist(y>=maxn?y%maxn:y));
            flag = 1;
            ll tmp = Exist(y);
            for(int i=mid;i>=0;i--){
                if(tmp%3 == 1){
                    out1[cnt1++] = Pow[i];
                }else if(tmp%3 == 2){
                    out2[cnt2++] = Pow[i];
                }
                tmp /= 3;
            }
            for(int i=mid+1;i<cur;i++){
                if(ans[i]<0){
                    out1[cnt1++] = -ans[i];
                }else if(ans[i]>0){
                    out2[cnt2++] = ans[i];
                }
            }
            sort(out1,out1+cnt1);
            sort(out2+1,out2+cnt2);
            ll sum1 = 0,sum2 = 0;
            for(int i=0;i<cnt1;i++){
                printf("%lld%c",out1[i],i==cnt1-1?'\n':' ');
                sum1 += out1[i];
            }
            printf("%lld\n",sum1);
            for(int i=0;i<cnt2;i++){
                printf("%lld%c",out2[i],i==cnt2-1?'\n':' ');
                sum2 += out2[i];
            }
            printf("%lld\n",sum2);
            //printf("Yes\n");
        }
        return;
    }
    ans[cur] = -Pow[cur];
    Find(cur+1,y-Pow[cur]);//-
    if(flag)return;
    ans[cur] = 0;
    Find(cur+1,y);//
    if(flag)return;
    ans[cur] = Pow[cur];
    Find(cur+1,y+Pow[cur]);//+
}
int main(){
    for(int i=1;i<=33;i++)Pow[i] = Pow[i-1]*3;
    //4782969
    int x = clock();
    dfs(0,0LL,0LL);
    //printf("%d\n",clock()-x);
    //printf("%d\n",tot);
    printf("%dms\n",clock()-x);
    /*int ans = 0;
    for(int i=1;i<=4e7;i++){
        int cnt = 0;
        for(int j=head[i];j;j=Next[j])cnt++;
        ans = max(ans,cnt);
    }
    printf("%d\n",ans);*/
    x = clock();
    ll n = 1e13;
    //printf("%lld %lld\n",n,Exist(n));
    //printf("%lld %lld\n",n,Exist(n));
    //scanf("%lld",&n);
    out2[0] = n;
    lmt = mid+2;
    while(!flag){
        Find(mid+1,n);
        ++lmt;
    }
    printf("%dms\n",clock()-x);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值