[题解]noip杂题题解

Posted 阿波罗2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[题解]noip杂题题解相关的知识,希望对你有一定的参考价值。


  这道题没有什么可说的,先统计,然后几次快排,答案就出来了

Code(整齐但不简洁的代码)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<fstream>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 using namespace std;
 8 typedef bool boolean;
 9 template<typename T>
10 inline void readInteger(T& u){
11     char x;
12     while(!isdigit((x = getchar())));
13     for(u = x - \'0\'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - \'0\');
14     ungetc(x, stdin);
15 }
16 int m, n;
17 int k, l;
18 int d;
19 typedef class Point{
20     public:
21         int x;
22         int y;
23         Point(const int x = 0, const int y = 0):x(x), y(y){}
24         boolean operator <(Point another) const{
25             if(this->x != another.x)    return this->x < another.x;
26             return this->y < another.y;
27         }
28 }Point;
29 inline Point readPoint(){
30     Point a;
31     readInteger(a.x);
32     readInteger(a.y);
33     return a;
34 }
35 typedef class MyData{
36     public:
37         int num;
38         int count;
39         MyData(const int num = 0):num(num){}
40 }MyDatas;
41 MyData *c_r;
42 MyData *c_l;
43 inline void init(){
44     readInteger(m);
45     readInteger(n);
46     readInteger(k);
47     readInteger(l);
48     readInteger(d);
49     c_r = new MyData[(const int)(m + 1)];
50     c_l = new MyData[(const int)(n + 1)];
51     memset(c_r, 0, sizeof(MyData) * (m + 1));
52     memset(c_l, 0, sizeof(MyData) * (n + 1));
53     for(int i = 1; i <= m; i++)    c_r[i].num = i;
54     for(int i = 1; i <= n; i++)   c_l[i].num = i;
55     for(int i = 1; i <= d; i++){
56         Point a = readPoint();
57         Point b = readPoint();
58         Point s = min(a, b);
59         if(a.x == b.x){
60             c_l[s.y].count++;
61         }else c_r[s.x].count++;
62     }
63 }
64 boolean cmpare(const MyData &a, const MyData &b){
65     return a.count > b.count;
66 }
67 boolean cmpare2(const MyData &a, const MyData &b){
68     return a.num < b.num;
69 }
70 inline void solve(){
71     sort(c_l + 1, c_l + n + 1, cmpare);
72     sort(c_r + 1, c_r + m + 1, cmpare);
73     sort(c_l + 1, c_l + l + 1, cmpare2);
74     sort(c_r + 1, c_r + k + 1, cmpare2);
75     for(int i = 1; i <= k; i++){
76         printf("%d ", c_r[i].num);
77     }
78     putchar(\'\\n\');
79     for(int i = 1; i <= l; i++){
80         printf("%d ", c_l[i].num);
81     }
82 }
83 int main(){
84     freopen("seat.in", "r", stdin);
85     freopen("seat.out", "w", stdout);
86     init();
87     solve();
88     return 0;
89 }


  这道题是有依赖的背包问题的裸题,一个主件最多有两个附件,而且附件没有属自己的附件,所以不用考虑树形dp

直接用普通的dp就行了,考虑4个状态

  1)只要主件

  2)只要主件和第一个附件

  3)只要主件和第二个附件

  4)要主件和它的两个附件

  如果某个主件的第二个附件不存在呢?不管,不会影响答案,如果特判的话很耗代码。

  如果dp到附件怎么办?不管,直接跳过

  如果你希望运行的速度更快,可以将n和每个v除以10,最后结果乘上10

Code(极其不简洁的代码)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<fstream>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 using namespace std;
 8 ///template starts
 9 typedef bool boolean;
10 template<typename T>
11 inline void readInteger(T& u){
12     char x;
13     while(!isdigit((x = getchar())));
14     for(u = x - \'0\'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - \'0\');
15     ungetc(x, stdin);
16 }
17 ///template ends
18 int n;
19 int m;
20 int *v;
21 int *w;
22 int *son[2];
23 int f[3201];
24 int result;
25 boolean *seced;
26 inline void init(){
27     readInteger(n);
28     readInteger(m);
29     n /= 10;
30     v = new int[(const int)(m + 1)];
31     w = new int[(const int)(m + 1)];
32     son[0] = new int[(const int)(m + 1)];
33     son[1] = new int[(const int)(m + 1)];
34     seced = new boolean[(const int)(m + 1)];
35     memset(seced, false, sizeof(boolean) * (m + 1));
36     memset(son[0], 0, sizeof(int) * (m + 1));
37     memset(son[1], 0, sizeof(int) * (m + 1));
38     for(int i = 1, a; i <= m; i++){
39         readInteger(v[i]);
40         v[i] /= 10;
41         readInteger(w[i]);
42         readInteger(a);
43         if(a == 0)    seced[i] = true;
44         if(son[0][a] == 0)    son[0][a] = i;
45         else son[1][a] = i;
46     }
47 }
48 inline void solve(){
49     seced[0] = true;
50     v[0] = w[0] = 0;
51     for(int i = 1; i <= m; i++){
52         if(seced[i]){
53             for(int j = n; j >= v[i]; j--){
54                 f[j] = max(f[j - v[i]] + v[i] * w[i], f[j]);
55                 int s = 0;
56                 int sv = 0;
57                 for(int k = 0; k < 2; k++){
58                     int r = v[son[k][i]] * w[son[k][i]];
59                     s += r;
60                     sv += v[son[k][i]];
61                     if(j >= v[i] + v[son[k][i]]){
62                         f[j] = max(f[j - v[i] - v[son[k][i]]] + v[i] * w[i] + r, f[j]);
63                     }
64                 }
65                 if(j >= v[i] + sv){
66                     f[j] = max(f[j - v[i] - sv] + v[i] * w[i] + s, f[j]);
67                 }
68             }
69         }
70     }
71     printf("%d", f[n] * 10);
72 }
73 int main(){
74     freopen("budget.in", "r", stdin);
75     freopen("budget.out", "w", stdout);
76     init();
77     solve();
78     return 0;
79 }


  这题有两个比较常见的做法,我只写其中一个,但还是都说说

1)Tarjan + 拓扑

  首先用Tarjan将所有的环(强连通分量)求出来,缩成一个点,求出它的最大值和最小值

接着从起点开始,进行拓扑排序,求出答案

2)spfa

  首先可以枚举一个断点,为了使答案最大,所以从起点到断点找到一个最小值,再从终点

到起点找到一个最大值,在这个最小值这个点买入,在最大值这个点卖出,用最大值减最小值

求出这个差价。

  如果每个断点都去跑两次spfa肯定会超时,所以就先用两次spfa预处理,第一次spfa在原

先的图上求出从起点出发,到i这个点的最短“距”,再从反向图中,从终点开始求出到点i这个点

的最长“距”

Code(比较复杂的代码)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<fstream>
  4 #include<cstring>
  5 #include<queue>
  6 #include<algorithm>
  7 using namespace std;
  8 ///template starts
  9 typedef bool boolean;
 10 template<typename T>
 11 inline void readInteger(T& u){
 12     char x;
 13     while(!isdigit((x = getchar())));
 14     for(u = x - \'0\'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - \'0\');
 15     ungetc(x, stdin);
 16 }
 17 typedef class Edge{
 18     public:
 19         int end;
 20         int next;
 21         Edge(const int end = 0, const int next = 0):end(end), next(next){}
 22 }Edge;
 23 typedef class MapManager{
 24     public:
 25         int ce;
 26         int *h;
 27         Edge *edge;
 28         MapManager():ce(0), h(NULL), edge(NULL){}
 29         MapManager(int limit, int points):ce(0){
 30             h = new int[(const int)(points + 1)];
 31             edge = new Edge[(const int)(limit + 1)];
 32             memset(h, 0, sizeof(int) * (points + 1));
 33         }
 34         inline void addEdge(int from, int end){
 35             edge[++ce] = Edge(end, h[from]);
 36             h[from] = ce;
 37         }
 38 }MapManager;
 39 ///template ends
 40 int n, m;
 41 int getData(boolean (*cmpare)(const int&, const int&), int a, int b){
 42     if((*cmpare)(a, b))    return a;
 43     return b;
 44 }
 45 int *prices;
 46 inline void spfa(MapManager& g, int start, int end, int *f, boolean* visited, boolean (*cmpare)(const int&, const int&)){
 47     memset(visited, false, sizeof(boolean) * (n + 1));
 48     queue<int> que;
 49     que.push(start);
 50     visited[start] = true;
 51     while(!que.empty()){
 52         int u = que.front();
 53         que.pop();
 54         visited[u] = false;
 55         for(int i = g.h[u]; i != 0; i = g.edge[i].next){
 56             int eu = g.edge[i].end;
 57             int d = getData(cmpare, f[u], prices[eu]);
 58             if((*cmpare)(d, f[eu])){
 59                 f[eu] = d;
 60                 if(!visited[eu]){
 61                     que.push(eu);
 62                     visited[eu] = true;
 63                 }
 64             }    
 65         }
 66     }
 67 }
 68 MapManager g;
 69 MapManager rev_g;
 70 int *maxdis;
 71 int *mindis;
 72 boolean *visited;
 73 boolean _min(const int &a, const int &b){    return a < b;    }
 74 boolean _max(const int &a, const int &b){    return a > b;    }
 75 inline void init(){
 76     readInteger(n);
 77     readInteger(m);
 78     g = MapManager(m * 2, n);
 79     rev_g = MapManager(m * 2, n);
 80     prices = new int[(const int)(n + 1)];
 81     maxdis = new int[(const int)(n + 1)];
 82     mindis = new int[(const int)(n + 1)];
 83     visited = new boolean[(const int)(n + 1)];
 84     memset(maxdis, 0, sizeof(int) * (n + 1));
 85     memset(mindis, 0x7f, sizeof(int) * (n + 1));
 86     for(int i = 1; i <= n; i++)
 87         readInteger(prices[i]);
 88     for(int i = 1, a, b, c; i <= m; i++){
 89         readInteger(a);
 90         readInteger(b);
 91         readInteger(c);
 92         g.addEdge(a, b);
 93         rev_g.addEdge(b, a);
 94         if(c == 2){
 95             g.addEdge(b, a);
 96             rev_g.addEdge(a, b);
 97         }
 98     }
 99 }
100 inline void solve(){
101     spfa(g, 1, n, mindis, visited, _min);
102     spfa(rev_g, n, 1, maxdis, visited, _max);
103     int result = 0;
104     for(int i = 2; i < n; i++){
105         result = max(result, maxdis[i] - mindis[i]);
106     }
107     printf("%d", result);
108 }
109 int main(){
110     freopen("trade.in", "r", stdin);
111     freopen("trade.out", "w", stdout);
112     init();
113     solve();
114     return 0;
115 }

(ps:为了防止写两遍spfa,所以一次spfa写得比较复杂)

以上是关于[题解]noip杂题题解的主要内容,如果未能解决你的问题,请参考以下文章

杂题记录及简要题解

『图论杂题题解』

历年NOIP选题题解汇总

noip2005提高组 题解

NOIP2013 DAY2题解

[NOIP2011]聪明的质监员 题解