NOIP2011选择客栈

n家客栈,1~n编号,每家按照某一种色调装饰,共k种,每家客栈都设有咖啡店,每家咖啡店均有各自的最低消费
两位游客,要求住在颜色相同,且不是同一个客栈,在两人的客栈间选择咖啡店(包括他们住的客栈),要求最低消费不超过p
求有多少种方案
输入格式:
第一行n,k,p,表示客栈的个数,色调数目,和可接受的最低消费的最高值
接下来n行,分别表示第i号客栈的色调和咖啡店的最低消费
输出格式
一个整数,表示方案总数
输入样例#1:
5 2 3
0 5
1 3
0 2
1 4
1 5
输出样例#1:
3
1 2 3 4 5
0 1 0 1 1
5 3 2 4 5

解题思路:
基本暴力思路是手动搜索出现过的,和当前位置颜色相同的客栈的位置
再依次判断
这样很慢。显然是不行的
然后我们可以想到这样一个思路(性质?):
如果我们累计不算当前位置,之前所出现过的当前颜色的总次数
那么如果可行,我们就要在总方案数上累加这个数。我们能注意到的是,每出现一个颜色,所需要检测的状态只增加了上一个同样颜色的位置和当前位置是否满足题意,需要方案数+1
但是对于是否可行这个问题,我们就需要开一个辅助数组记录当前颜色上一次出现的位置
但是如果不可行呢?继续向前,上上一个位置,上上上一个位置都不可行呢?
这就提示我们要开第二个辅助数组,记录方案数,这个数组的值只有满足了上一个位置和当前位置这一决策符合题意时才能更新,这样保证了我们每次累加的方案数永远都是合法的未知的方案数
于是又出现了新的问题:拿什么值去更新?
我们可以发现,若是上一个位置和当前位置可行,那么当前位置与此前所有该颜色的位置匹配都可行,这也就是说,在该情况下,我们累计的是不算当前位置,当前颜色出现过的总次数
这也就是我们要拿来去更新辅助数组2的值
于是我们再开第三个辅助数组,记录不算当前位置,该颜色出现过的次数
接着代码的实现就非常容易了

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 2000086;
 4 struct enkidu {
 5     int color, cost;
 6 }a[maxn];
 7 int n, k, p;
 8 int color_cnt[60], color_last[60];
 9 int color_num[60];
10 int last_acc;
11 int ans = 0;
12 bool vis[maxn];
13 
14 inline int read() {
15     int x = 0, y = 1;
16     char ch = getchar();
17     while(!isdigit(ch)) {
18         if(ch == '-') y = -1;
19         ch = getchar();
20     }
21     while(isdigit(ch)) {
22         x = (x << 1) + (x << 3) + ch - '0';
23         ch = getchar();
24     }
25     return x * y;
26 }
27 
28 int main() {
29     memset(color_cnt, 0, sizeof(color_cnt));
30     memset(color_num, 0, sizeof(color_num));
31     n = read(), k = read(), p = read();
32     for(int i = 1; i <= n; ++i)
33         a[i].color = read(), a[i].cost = read();
34     for(int i = 1; i <= n; ++i) {
35         int cl = a[i].color;
36         if(a[i].cost <= p) last_acc = i;//求上一个小于p的位置 
37           if(vis[cl]) {
38             if(color_last[cl] <= last_acc) //若上一个当前颜色的位置比上一个小于p的位置小,则说明目前当前颜色出现过的总次数将是要累加的答案 
39                 color_num[cl] = color_cnt[cl];//否则要累加的是上一个满足要求的当前颜色的位置记录的出现过的次数 
40             color_last[cl] = i;//更新上一个当前颜色出现的位置 
41             ans += color_num[cl];//累加答案 
42             color_cnt[cl]++;//统计到目前为止,当前颜色出现的次数 
43         }
44         else if(!vis[cl]) {//若当前颜色从未见过 
45             color_last[cl] = i;
46             color_cnt[cl]++;
47             vis[cl] = 1;
48         }
49     }
50     cout << ans << '\n';
51     return 0;
52 }

 

posted @ 2018-07-31 22:04  YuWenjue  阅读(170)  评论(0编辑  收藏  举报