2019-icpc-taipei

 ACM  构造 󰈭 1016字

I 已知两数差,求所有可能的原本的序列

原序列长度小于等于62,序列值小于1000且两两不同。

如果已经还原出了原序列中的$i$个值,那么除去这i个值两两的差之后,差值集合中最大的数便可以基本确定下来:这个数一定是原序列中的某个数与a[1] = 0或者a[n]作差得到的,这点由“最大”保证。

那么检查这个最大的数出现的次数,如果次数超过2,则不满足两两不同的要求;如果次数为2,直接根据a[1], a[n]可以更新两个值进行原序列;如果次数仅为1,则需要分别递归考虑一番。

每次插入一个新值的时候要进行若干判断

可能因此条件较为严苛?导致次数仅为1的2^n递归也在可以接受的范围之内;具体次数证明并没有太想得到

cpp
 1
 2const int maxn = 5e3 + 10;
 3
 4int n, a[maxn], M;
 5multiset<int, greater<int>> s, temp;
 6multiset<int, greater<int>>::iterator it;
 7
 8set<int> res;
 9vector<set<int>> ans;
10
11void sove(){
12    //从s集合中挑选一个进行尝试构造
13    if(s.empty()){ans.push_back(res); return;}
14
15    int now = *s.begin(); int cnt = (int)s.count(now);
16    if(cnt > 2) return ;
17    else if(cnt == 2){
18        int x = now, y = M - now;
19        //保证两两不同
20        if(res.find(x) != res.end() || res.find(y) != res.end()) return;
21        
22        temp = s; int fail = 0;
23        //遍历那些已经确定好的值
24        for(auto pre: res){
25            it = temp.find(abs(pre - x));
26            if(it == temp.end()){fail = 1; break;}
27            else{temp.erase(it);}
28
29            it = temp.find(abs(pre - y));
30            if(it == temp.end()){fail = 1; break;}
31            else{temp.erase(it);}
32        }
33        if(fail) return ;
34        it = temp.find(abs(x - y));
35        if(it == temp.end()) return ;
36        temp.erase(it); 
37
38        s = temp; res.insert(x); res.insert(y);
39        sove(); res.erase(x); res.erase(y);
40    }
41    else{
42        //个数为1
43        multiset<int, greater<int>> s0 = s;
44        int x = now;
45        if(res.find(x) == res.end()){
46            int fail = 0; temp = s0;
47            for(auto pre: res){
48                it = temp.find(abs(x - pre));
49                if(it == temp.end()){fail = 1; break;}
50                else temp.erase(it);
51            }
52            if(!fail){
53                res.insert(x); s = temp; sove();
54                res.erase(x);
55            }
56        }
57        
58        x = M - now;
59        if(res.find(x) == res.end()){
60            int fail = 0; temp = s0;
61            for(auto pre: res){
62                it = temp.find(abs(x - pre));
63                if(it == temp.end()){fail = 1; break;}
64                else temp.erase(it);
65            }
66            if(!fail){
67                res.insert(x); s = temp; sove();
68                res.erase(x);
69            }
70        }
71    }
72}
73
74int main(){
75    // Fastin;
76    scanf("%d", &n); for(int i = 0; i < n * (n - 1) / 2; i++){
77        int x; scnaf("%d", &x); s.insert(x);
78        if(x > 999){ puts("0"); return 0;}
79        M = max(M, x);
80    }
81    if((int)s.count(M) > 1){ puts("0"); return 0;}
82
83    a[n] = M; s.erase(a[n]); res.insert(0); res.insert(M);
84    sove();
85    int top = (int)(unique(ans.begin(), ans.end()) - ans.begin());
86    sort(ans.begin(), ans.begin() + top);
87    printf("%d\n", top);
88    for(int i = 0; i < top; i++){
89        for(auto x: ans[i]) printf("%d ", x);
90        printf("\n");
91    }
92
93    return 0;
94}

luogu 两数和

给出两数和,问是否存在一个可行原序列。

原序列长度不超过10.

将两数和的集合从小到大开始考虑,通过最小的和数可以枚举出a[1], a[2]的值,接下来在集合中删除这些值,剩下的元素中最小值必然就是a[1]+a[3],据此可以得到a[3]的值,重复操作即可不断向后构造。

cpp
 1multiset<int> s0, s;
 2
 3int a[20];
 4multiset<int>::iterator it;
 5vector<int> vec;
 6
 7int main(){
 8     Fastin;
 9    int n; 
10    while(~scnaf("%d", &n)){
11        s0.clear();
12        fro(int i = 0; i < n * (n - 1) / 2; i++){
13            int x; scanf("%d", &x); s0.insert(x);
14        }
15        
16        int ok = 0;
17        for(a[1] = 0; a[1] <= *s0.begin() - a[1]; a[1]++){
18            int fail = 0;
19            s = s0;
20            for(int i = 2; i <= n; i++){
21                a[i] = *s.begin() - a[1];
22                if(a[i] < 0 || a[i] < a[i - 1]){fail = 1; break;}
23                for(int j = 1; j < i; j++){
24                    it = s.find(a[j] + a[i]);
25                    if(it == s.end()){fail = 1; break;}
26                    s.erase(it);
27                }
28                if(fail) break;
29            }
30            if(!fail){
31                for(int i = 1; i <= n; i++) printf("%d%c", a[i], " \n"[i == n]);
32                ok = 1;
33            }
34        }
35        if(!ok) puts("Impossible");
36    }
37} 
嗨! 这里是 rqdmap 的个人博客, 我正关注 GNU/Linux 桌面系统, Linux 内核 以及一切有趣的计算机技术! 希望我的内容能对你有所帮助~
如果你遇到了任何问题, 包括但不限于: 博客内容说明不清楚或错误; 样式版面混乱; 加密博客访问请求等问题, 请通过邮箱 rqdmap@gmail.com 联系我!
修改日志
  • 2023-09-01 18:14:49 单独划分ACM专题; 移动部分博客进入黑洞归档
  • 2023-05-29 23:05:14 博客结构与操作脚本重构
  • 2023-05-08 21:44:36 博客架构修改升级
  • 2022-11-16 01:27:34 迁移老博客文章内容