ZOJ 3931 Exact Compression

23 篇文章 0 订阅
21 篇文章 0 订阅

 

Exact Compression


Time Limit: 2 Seconds      Memory Limit: 65536 KB


 

Huffman Code is a commonly used optimal prefix code. Here is a simple introduction to Huffman Coding from wikipedia.

The technique works by creating a binary tree of nodes. These can be stored in a regular array, the size of which depends on the number of symbols, S. A node can be either a leaf node or an internal node. Initially, all nodes are leaf nodes, which contain the symbol itself, the weight (frequency of appearance) of the symbol and optionally, a link to a parent node which makes it easy to read the code (in reverse) starting from a leaf node. Internal nodes contain symbol weight, links to two child nodes and the optional link to a parent node. As a common convention, bit '0' represents following the left child and bit '1' represents following the right child. A finished tree has up to n leaf nodes and n-1 internal nodes. A Huffman tree that omits unused symbols produces the most optimal code lengths.

The simplest construction algorithm uses a priority queue where the node with lowest weight is given highest priority:

  1. Create a leaf node for each symbol and add it to the priority queue.
  2. While there is more than one node in the queue:
    1. Remove the two nodes of highest priority (lowest weight) from the queue
    2. Create a new internal node with these two nodes as children and with weight equal to the sum of the two nodes' weight.
    3. Add the new node to the queue.
  3. The remaining node is the root node and the tree is complete.

 

For example, one day Edward wanted to send a string "aeaaaageqqqq" to his best friend Min as a gift. There are four symbols 'a', 'e', 'g', 'q' and so their weights are 5, 2, 1, 4.

Firstly Edward merged the two symbols 'e' and 'g' with lowest weights and get a node with weight of 1 + 2 = 3. Then Edward merged two nodes of weight 3 and 4 and get a node with weight 3 + 4 = 7. Finally Edward merged the last two nodes. If we distribute the prefix '0' to the smaller node, Edward can get the code of four symbols '0', '101', '100', '11'.

If we know the number of occurrences of each character in some content, can we compress it using Huffman Code and the number of '0's is exactly E? More precisely, let the number of occurrences of the i-th character be Fi and the number of '0's in its huffman code be Ci, we want to know whether there exists a specific huffman code satisfying that the sum of Fi * Ci is equal to E.

Input

There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:

The first line of each case contains a positive integer S (2 ≤ S ≤ 128), indicates the number of different symbols.

The second line contains exactly S integers Fi (1 ≤ Fi ≤ 1000), indicates the number of occurrence of each symbol.

The third line contains a non-negative integer E (0 ≤ E ≤ 108), indicates the expected number of '0's.

Output

For each case, please output "Yes" if it can be satisfied and "No" in otherwise.

Sample Input

3
2
1 3
2
2
1 3
3
4
5 2 1 4
11

Sample Output

No
Yes
Yes

Hint

The possible huffman codes for examples:

  1. None
  2. '1', '0'
  3. '1', '001', '000', '01'
【题意】
有n(128)个节点,我们要构造一棵哈夫曼树。
然后给每个节点做编码。
我们最后会求和——
每个点的点权*根节点到这个节点路径上0的个数是否恰好为m(m∈[0,1e8])
实际上可达的数值上限只有448000
【时间复杂度&&优化】
O(T * 128*450000)
动态滚动DP上界显然是DP中非常重要也必不可少的技巧。

 

每次需要比较排序时还可以用自定义函数从小到大进行排序,代码如下:

 

struct cmp{
    bool operator()(const int& x,const int& y ){
        return x > y;
    }
};
priority_queue<int,vector<int>,cmp> que;

 

 

 

跟大神一起商讨写的的代码

运行的时间 90ms   内存  464

(ps:两个队列互相滚动存取,在压入队列之前有一个判重的的语句,重复的不再压入队列)

 

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int a[150],b[150],s,E;
int bfs(int x)
{
    priority_queue<int> q1,q2;
    q1.push(0);
    int tmp,tk,flag=0,i=x,kk;
    while(i<s-1){
        if (i%2==0){
            while(!q1.empty()){
                tmp=q1.top();
                q1.pop();
                if (q1.size() && q1.top()==tmp) q1.pop();
                if(tmp+a[i]<=E){
                    q2.push(tmp+a[i]);
                }
                if(tmp+b[i]<=E){
                    q2.push(tmp+b[i]);
                }
            }
        }
        else {
            while(!q2.empty()){
                tmp=q2.top();
                q2.pop();
                if (q2.size() && q2.top()==tmp) q2.pop();
                if(tmp<=E){
                    q1.push(tmp+a[i]);
                }
                if(tmp+b[i]<=E){
                    q1.push(tmp+b[i]);
                }
            }
        }
        i++;
    }
    if (i%2==0){
        while(q1.size()){
            kk=q1.top();
            q1.pop();
            if(kk==E){
                 flag=1;
                 break;
            }
        }
    }
    else{
        while(q2.size()){
            kk=q2.top();
            q2.pop();
            if(kk==E){
                 flag=1;
                 break;
            }
        }
    }

    if(flag)    return 1;
    else    return 0;
}

int main()
{
    int t,d;
    scanf("%d",&t);
    while(t--){
        memset(b,0,sizeof(b));
        memset(a,0,sizeof(a));
        priority_queue<int,vector<int>,greater<int> > que;  //从小到大排序 
        scanf("%d",&s);
        for(int i=0;i<s;i++){
            scanf("%d",&d);
            que.push(d);
        }
        scanf("%d",&E);
        int sk=s-1,_num=0;
        while(sk!=0){
            a[_num]=que.top();
            que.pop();
            sk--;
            b[_num]=que.top();
            que.pop();
            sk--;
            que.push(a[_num]+b[_num]);
            _num++;
            sk++;
        }
        if(bfs(0))  printf("Yes\n");
        else        printf("No\n");
    }
    return 0;
}

 

 

用容器map映射标记重复与否

运行时间180ms  内存 1064

 

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
int a[150],b[150],s,E;
map<long long,bool> vis;
int bfs(int x)
{
    queue<int> q1,q2;
    q1.push(0);
    int tmp,tk,flag=0,i=x,kk;
    while(i<s-1){
        vis.clear();
        if (i%2==0){
            while(!q1.empty()){
                if(q1.front()+a[i]<=E && vis[q1.front()+a[i]]==0){
                    q2.push(q1.front()+a[i]);
                    vis[q1.front()+a[i]]=1;
                }
                if(q1.front()+b[i]<=E && vis[q1.front()+b[i]]==0){
                    q2.push(q1.front()+b[i]);
                    vis[q1.front()+b[i]]=1;
                }
                q1.pop();
            }
        }
        else {
            while(!q2.empty()){
                if(q2.front()+a[i]<=E && vis[q2.front()+a[i]]==0){
                    q1.push(q2.front()+a[i]);
                    vis[q2.front()+a[i]]=1;
                }
                if(q2.front()+b[i]<=E && vis[q2.front()+b[i]]==0){
                    q1.push(q2.front()+b[i]);
                    vis[q2.front()+b[i]]=1;
                }
                q2.pop();
            }
        }
        i++;
    }
    if (i%2==0){
        while(q1.size()){
            kk=q1.front();
            q1.pop();
            if(kk==E){
                 flag=1;
                 break;
            }
        }
    }
    else{
        while(q2.size()){
            kk=q2.front();
            q2.pop();
            if(kk==E){
                 flag=1;
                 break;
            }
        }
    }
    if(flag)    return 1;
    else    return 0;
}

int main()
{
    int t,d;
    scanf("%d",&t);
    while(t--){
        memset(b,0,sizeof(b));
        priority_queue<int,vector<int>,greater<int> > que;
        scanf("%d",&s);
        for(int i=0;i<s;i++){
            scanf("%d",&d);
            que.push(d);
        }
        scanf("%d",&E);
        int sk=s-1,_num=0;
        while(sk!=0){
            a[_num]=que.top();
            que.pop();
            sk--;
            b[_num]=que.top();
            que.pop();
            sk--;
            que.push(a[_num]+b[_num]);
            _num++;
            sk++;
        }
        if(bfs(0))  printf("Yes\n");
        else        printf("No\n");
    }
    return 0;
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黎轩栀海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值