之前也有训练,只是没时间写博客了,今天正好有时间,来写一下
洛谷 —— 分组
P4447 [AHOI2018初中组] 分组 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
上来就是一道绿题啊
本题是要把一些人分成几组,然后使得人数最少的组人最多,也就是最小的最大值,我们应该可以想到二分和队列。因为他每组中人数的实力必须是连续的,我们可以定义一个数组来记录每组当前实力的最大值。
1、我们先对实力进行排序
cin>>n;
for(int i =1;i<=n;++i)cin>>a[i];
sort(a+1 , a+1+n);
2、记录每一组实力最大的数,然后枚举每一个ai,找到某一组的实力最大的数是ai-1,之后ai就可以进入这一组,同时用sz记录每一组的人数
for(int i =1;i<=n;++i){
int j = search(a[i]-1); //j表示找到ai -1的这个实力在第j组
if(j == -1 || cnt == 0){ //如果没有找到,或者cnt还等于0 , 就新开一组 , cnt是有多少组
ma[cnt] = a[i];
sz[cnt++]++;
}
else {
ma[j] = a[i];
sz[j]++;
}
}
3、二分查找
int search(int x){
int l = 0 , r = cnt-1;
while(l < r){
int mid = (l+r+1)>>1;
if(ma[mid] <= x)l = mid;
else r = mid-1;
}
return ma[l] ==x ? l : -1;
}
之后这题就基本解决了,以下是完整代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int N = 1e5+9;
int a[N];
bool vis[N];
int n;
int ma[N] , sz[N];
int cnt;
int search(int x){
int l = 0 , r = cnt-1;
while(l < r){
int mid = (l+r+1)>>1;
if(ma[mid] <= x)l = mid;
else r = mid-1;
}
return ma[l] ==x ? l : -1;
}
void solve(){
az
for(int i =1;i<=n;++i){
int j =search(a[i] - 1); //找比ai小一的数
if(j == -1 || cnt == 0){
ma[cnt] = a[i];
sz[cnt++]++;
}
else {
ma[j] = a[i];
sz[j]++;
}
}
int maxn = INF;
for(int i =0;i<cnt;++i)maxn = min(maxn , sz[i]);
cout<<maxn;
}
signed main(){
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int _ = 1;
while(_--){
solve();
}
return 0;
}
洛谷 —— 国王游戏
P1080 [NOIP2012 提高组] 国王游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这题是一道找规律的贪心题,但是还需要高精度才能通过,由于我不会高精度,所以这里给出60分代码
1、数学推导:
假设只有两个大臣 此时的最多的大臣的金币是max( ai/bi , (a[i]+a[i+1])/b[i+1])
若将两个人交换位置 , 此时最多的金币是 max(a[i+1]/b[i+1] , (a[i+1] + a[i])/b[i])
那么交换前后哪个数比较小呢 ? 也就是比较 (a[i]+a[i+1])/b[i+1] 和 (a[i+1] + a[i])/b[i] 这两个数的大小,可以看到,这两个数只有分母是不同的,也就是说 ,如果说后面的人右手越大,就越不要交换,什么意思呢?也就是按照右手大小排序,右手越小的就越要在前面,那么这题的关键就找到了,就是对右手从小到大排序。
2、排序。 可以用结构体
struct hand{
int l ,r;
}a[N];
bool cmp(hand x , hand y){
return x.l * x.r < y.l * y.r;
}
cin>>n;
cin>>L>>R;
int ans = -1;
for(int i =1;i<=n;++i)cin>>a[i].l>>a[i].r;
sort(a+1 , a+1+n ,cmp);
完整代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int N = 1e5+9;
int n;
int L , R;
struct hand{
int l ,r;
}a[N];
bool cmp(hand x , hand y){
return x.l * x.r < y.l * y.r;
}
void solve(){
cin>>n;
cin>>L>>R;
int ans = -1;
for(int i =1;i<=n;++i)cin>>a[i].l>>a[i].r;
sort(a+1 , a+1+n ,cmp);
// for(int i =1;i<=n;++i){
// cout<<a[i].l<<' '<<a[i].r<<endl;
// }
int sum = L;
for(int i =1;i<=n;++i){
sum *= a[i].l;
int t = sum/(a[i].r*a[i].l);
ans = max(ans , t);
}
cout<<ans;
}
signed main(){
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int _ = 1;
while(_--){
solve();
}
return 0;
}
洛谷 —— 单词接龙
这题可以用一个dfs,那么如何dfs有两个问题 : 一是如何判断谁跟谁可以接龙 二是每个单词都只次接龙的机会
1、我们可以先预处理一下所有的单词,用g[i][j]表示j可以接在i后面 , 就类似图论中的有向图一样
for(int i =1;i<=n;++i){
for(int j =1;j<=n;++j){
string x =a[i] , y = a[j];
for(int k =1;k<=min(x.size() , y.size());++k){
if(x.substr(x.size()-k , k) == y.substr(0,k)){
g[i][j] = k;
break;
}
}
}
}
2、我们可以用一个数组来记录每个单词接龙次数
那么完整代码如下
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int N = 25;
int n , res;
string a[N];
int used[N];
int g[N][N];
void dfs(string dargon , int x){
res = max(res , (int)dargon.size());
used[x]++;
for(int i =1;i<=n;++i){
if(g[x][i] && used[i] < 2){
dfs(dargon + a[i].substr(g[x][i]) , i);
}
}
used[x]--; //回溯
}
void solve(){
cin>>n;
for(int i =1;i<=n;++i)cin>>a[i];
char st;cin>>st;
for(int i =1;i<=n;++i){
for(int j =1;j<=n;++j){
string x = a[i] , y = a[j];
for(int k =1;k<=min(x.size() , y.size())-1;++k)
if(x.substr(x.size()-k , k) == y.substr(0,k)){
g[i][j] = k;
break; //因为龙要长,所以重复的长度要短
}
}
}
for(int i =1;i<=n;++i){
if(a[i][0] == st){
dfs(a[i],i);
}
}
cout<<res;
}
signed main(){
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int _ = 1;
while(_--){
solve();
}
return 0;
}
洛谷 —— 字串变换
P1032 [NOIP2002 提高组] 字串变换 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
由于最短我们可以想到用层序遍历的解法,那么这题就很好解决了。那么本题的难点就是字符串的变换,我们可以用find 和 replace来解决
1、bfs部分内容
q.push({n,0}); //最开始是n,走了0步
while(!q.empty()){
auto now = q.front();
q.pop();
if(now.step >= 11){ //当大于等于11步的时候就返回-1了
return -1;
}
if(now.s == m){ //找到字串后返回此时的步骤 , 由于是bfs,一定是最小的步骤
return now.step;
}
for(int i =0;i<idx;++i){
int id = now.s.find(a[i]); //进行查找
while(id != -1){ //注意是while
string st = now.s; //用一个临时字符串代替 , 因为主字符串不能变化
st.replace(id,a[i].size(),b[i]);
if(mp[st] == 0){
q.push({st,now.step+1});
mp[st]++; //这里稍后在说
}
id = now.s.find(a[i],id+1);
}
}
}
return -1;
2、那么根据以上做法,我们会发现超出内存了,大家可以注意到上面的mp[st]++,这是干什么的? 因为bfs太多的字符串进入我们的队列后,超出了内存,而很多的字符串却是相同的,对于相同的字符串,我们只需要遍历一次。所以开一个哈希表来进行查重,这里使用的是map
完整代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
const int N = 15;
string a[N] , b[N];
map<string ,int >mp;
struct word
{
string s;
int step;
};
queue<word>q;
string n , m;
int idx;
int bfs(){
q.push({n,0}); //最开始是n,走了0步
while(!q.empty()){
auto now = q.front();
q.pop();
if(now.step >= 11){
return -1;
}
if(now.s == m){
return now.step;
}
for(int i =0;i<idx;++i){
int id = now.s.find(a[i]);
while(id != -1){
string st = now.s;
st.replace(id,a[i].size(),b[i]);
if(mp[st] == 0){
q.push({st,now.step+1});
mp[st]++;
}
id = now.s.find(a[i],id+1);
}
}
}
return -1;
}
void solve(){
cin>>n>>m; //要把n变成m
while(cin>>a[idx]>>b[idx])idx++;
int ans = bfs();
if(ans == -1)cout<<"NO ANSWER!";
else cout<<ans;
}
signed main(){
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int _ = 1;
while(_--){
solve();
}
return 0;
}
洛谷 —— 路标设置
P3853 [TJOI2007] 路标设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这题很显然最大的最小值,二分答案,就类似于进击的奶牛这道题进击的奶牛
既然是二分答案,那么就很明确了,难点是如何进行判断
1、哪一种二分模板
我们可以想一下,空旷指数越大,那么路标也就更容易放完,也就是题意更好满足,空旷指数变小,题意就更难满足 , 所以说,我们需要找一个临界的右边点 (因为左边都是不满足的 , 右边都是满足的 ,找一个最小的满足点)
while(l < r){
int mid = (l+r)>>1;
if(check())r = mid;
else l = mid+1;
}
2、如何写check函数
我们可以通过路标的数量进行检查,我们可以先预处理出来一个差分数组,表示的就是两个路标之间的差值 , 如果路标之间的差值大于 x , 我们就加若干个路标,最后统计一下加的路标是否大于我们的题目中所给的路标数量
int cnt = 0;
for(int i =1;i<=n;++i){
if(dif[i] > x){
cnt += (dif[i]/x) - (dif[i]%x == 0);
}
if(cnt > m )return false;
}
if(cnt <=m)return true;
else return false;
完整代码
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define INF 0x3f3f3f3f
const double pi = 3.14;
// #define x first
// #define y second
#define int long long
// #define ll long long
typedef pair<int,int> pii;
// const int mod = 998244353;
int l , n , m;
const int N = 100010;
int a[N] ,dif[N];
bool check(int x){
int cnt = 0;
for(int i =1;i<=n;++i){
if(dif[i] > x){
cnt += (dif[i]/x) - (dif[i]%x == 0);
}
if(cnt > m )return false;
}
if(cnt <=m)return true;
else return false;
}
void solve(){
cin>>l>>n>>m;
for(int i =1;i<=n;++i)cin>>a[i];
sort(a+1 , a+1+n);
for(int i =1;i<=n;++i)dif[i] = a[i] - a[i-1];
int ll = 0 , r = l;
while(ll < r){
int mid = (ll+r)>>1;
if(check(mid))r = mid;
else ll = mid+1;
}
cout<<r;
//n-1份路 n-1份路加起来是l
}
signed main(){
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int _ = 1;
while(_--){
solve();
}
return 0;
}