离散化讲解
离散化例题
例1
题目链接
题目大意
(我说详细点,你就不用翻译了)
n个人,每个人会一种语言;m部电影,每部电影的声音语言和字幕语言都不一样。若某人只能听懂,那么此人将非常满意;若某人只能看懂,那么他将勉强满意。
输入:
输入n,表示人的个数
输入n个数,代表不同语言的序号
输入m,表示电影个数
输入m个数,代表每部电影的声音语言
输入m个数,代表每部电影的字幕语言
输出:
一部电影的序号,此电影让尽可能多的人非常满意,若非常满意的人数相同,那么就再让尽可能多的人勉强满意,若都相同,那么输出任意一个满足条件的即可。
解题思路
大佬给出了板子了,比较好弄了。
直接说一下我的思路吧。
step1:
输入。a,b,c数组分别存人的语言,电影声音语言,电影字幕语言。
step2:
离散化,把a,b,c放在一个数组中,进行去重排序,再为语言重新安排序号。(就是大佬讲解中的板子)
unique函数简单讲解
step3:
遍历每一部电影,对于每一部电影的声音语言和字幕语言,都去人的语言中找有几个,并存在结构体中分开记录,把电影的序号也记下来,因为之后排序后打乱顺序,所以记录下来是必须的。
至于如何找人中符合电影语言的个数,upper-lower就是了。
step4:
对结构体排序,输出。
排序的时候要自定义一个cmp函数,按照输出条件从大到小排序,输出第一个就行。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
const int N=3*M;
struct Movie{
int very,almost,idx;
}mov[M];
int a[M],b[M],c[M];
int d[N];
bool cmp(Movie a,Movie b){
if(a.very!=b.very) return a.very>b.very;
else return a.almost>b.almost;
}
int main(){
int n,m,num=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++) cin>>b[i];
for(int i=1;i<=m;i++) cin>>c[i];
//离散化
for(int i=1;i<=n;i++) d[++num]=a[i];
for(int i=1;i<=m;i++){
d[++num]=b[i];
d[++num]=c[i];
}
sort(d+1,d+num+1);
num=unique(d+1,d+num+1)-d-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(d+1,d+num+1,a[i])-d;
for(int i=1;i<=m;i++){
b[i]=lower_bound(d+1,d+num+1,b[i])-d;
c[i]=lower_bound(d+1,d+num+1,c[i])-d;
}
sort(a+1,a+n+1);
for(int i=1;i<=m;i++){
int cnt1=upper_bound(a+1,a+n+1,b[i])-lower_bound(a+1,a+n+1,b[i]);
int cnt2=upper_bound(a+1,a+n+1,c[i])-lower_bound(a+1,a+n+1,c[i]);
mov[i].very=cnt1;
mov[i].almost=cnt2;
mov[i].idx=i;
}
sort(mov+1,mov+m+1,cmp);
cout<<mov[1].idx<<endl;
} 例2
题目链接
题目大意
青蛙从0开始蹦,问最少踩到多少石子能过河。
输入L,代表桥长;
输入S,T,M,分别代表最短跳跃距离,最长跳跃距离,石子数目;
输入M个数,表示石子位置。
解题思路
100个石子,1e9的桥,显然要离散化。
比较新颖的是这个不是板子了,这应该如何离散化?
100个石子,1e9的桥,显然很多地方是没有石子的,没有石子的路如果很长,需要好几步才能跳到,跳跃过程中都不会踩到石子或者跨过石子,那我们不妨把他缩小,让青蛙一步到达。因为没踩到或跨越石子的跳跃都是无效的,就比如10的位置有个石子,青蛙从0跳到9的过程,一步跳跃和多步跳过去,对最终结果是没影响的,没经过石子啊。
因此,我们将所有石子间距大于T的石子间距都缩小为T,缩小为T的意义在于,青蛙可以一步跳达或者分两步跨越,本质上包含了未离散化时的状态,跳达与跨越的状态。
离散化实现
sort(pos+1,pos+m+1);
for(int i=1;i<=m;i++)
dis[i]=pos[i]-pos[i-1];
for(int i=1;i<=m;i++){
dis[i]=min(t,dis[i]);
endd+=dis[i];
pos[i]=pos[i-1]+dis[i];
f[pos[i]]=1;
}数组介绍:
pos[i]表示第i个石子的位置;
dis[i]表示第i个石子与前一个石子的距离;
f[i]表示i位置是否有石子。
变量介绍:
endd用于保存最终桥长。
过程实现:
step1:因为输入的石子位置不一定是按照从小到大输入的,所有先排一下序。
step2:循环计算相邻石子的间距。
step3:循环,为了更新石子位置;若间距大于了T,间距赋值为T,否则不变;桥长(从0开始的)加上新间距;更新此石子的位置,更新位置是要通过前一个石子的位置加上新间距确定的;同时标记更新完的石子所在位置。
dp实现
不妨把起点0位置看作dp的终点,因为0点是确定的而跳过桥的终点其实是不确定的。
反向dp就行了。
挺像板子的,就是把过程反过来了而已。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int dp[N];
int l,s,t,m,endd;
int pos[N],dis[N],f[N];
int main(){
cin>>l;
cin>>s>>t>>m;
for(int i=1;i<=m;i++){
cin>>pos[i];
}
if(s==t){
int ans=0;
for(int i=1;i<=m;i++)
if(pos[i]%s==0) ans++;
cout<<ans<<endl;
return 0;
}
sort(pos+1,pos+m+1);
for(int i=1;i<=m;i++)
dis[i]=pos[i]-pos[i-1];
for(int i=1;i<=m;i++){
dis[i]=min(t,dis[i]);
endd+=dis[i];
pos[i]=pos[i-1]+dis[i];
f[pos[i]]=1;
}
for(int i=endd;i>=0;i--){
dp[i]=100;
for(int j=s;j<=t;j++){
dp[i]=min(dp[i],dp[i+j]+f[i]);
}
}
cout<<dp[0]<<endl;
return 0;
}注意
我又没AC,忘了特判特殊情况了!!!
s=t的时候特判一下,要不就只能过80!!!
真ex!!!

京公网安备 11010502036488号