NC235254 晾衣服
https://ac.nowcoder.com/acm/problem/235254
来源:牛客网
首先看到这题的时候想到的是贪心而并非是二分答案,一开始的贪心的思路是从大到小湿度的衣服运用烘干依次晒干(当然,能自然风干的不需要再烘干,只需要去记录一个自然风干的值就行)
但后来发现自己的贪心思路有误,最大湿度的衣服不一定烘干到底,就比如一分钟烘干10度,而衣服湿度为12,那么剩下的2湿度可以留给自然烘干—>那么正确思路应该是对数组中每次最大湿度的衣服使用烘干机,并不断更新数组中的元素---->即本质为降低平均湿度,使得自然风干的衣服最大化
但题目数据范围实在太大,一次次减去包被超时,所以兜兜转转还是回到二分答案qwq
那二分答案的右边界是什么呢?注意到k最小为1,最坏情况下我们可以选取数组最大值以1度慢慢烘干,其他自然风干的时间作为右边界
下面是代码
感觉有一个坑点就是烘干是包含风干的,在check中减去风干的之后,需要除以k-1而非k
#include<bits/stdc++.h>
#include<cmath>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int len, n, m;
int s[N];
int d[N];
int sum[N];
int tempd[N];
int check(int mid)
{
//cout<<mid<<endl<<endl;
int cnt=0;
for(int i=0;i<n;i++)
{
double tempm=m;
if(s[i]<=mid) continue;
double now=s[i]-mid;
cnt+=ceil(now/(tempm-1));
//cout<<cnt<<endl;
if(cnt>mid) return false;
}
return true;
}
signed main()
{
int r = 0;
cin >> n;
for (int i = 0; i < n; i++)
cin >> s[i],r=max(r,s[i]);
cin >> m;
if(m==1) cout<<r<<endl,exit(0);
int l = 1;
r=r+10;//防止边界问题
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid))
r = mid;
else
l = mid + 1;
}
cout << l << endl;
}
[USACO 2010 Feb S]Chocolate Eating
链接:https://ac.nowcoder.com/acm/problem/24724
来源:牛客网
坐半天发现好多地方是错的,心碎了qwq,思路的不同导致要处理多好多细节,以后再做之前一定要考虑好,对于我将要处理的对象,我要考虑到的细节,以及我是否能通过改变对象去简化操作。
在check中其实我不应该去枚举每个巧克力,记录每个在哪天吃,因为尽管使得每块巧克力的安排能满足二分的mid,但我要去判断在安排完所有巧克力后,昨天剩余的能否去满足剩下的天数,那么我就要多次进行除二判断了
个人理解:对于天数,他有一个明确的范围,变化清晰,且在天数结束后,巧克力必定被吃完,所以枚举天数为优,而对于巧克力,其对应的快乐指数不定,细节处理比较繁杂。(也可能是我蔡)qwq
最重要的一点是我没考虑到,对于每次二分,我的程序都会不管三七二十一去记录巧克力的使用日期,导致我最后的ans数组不一定是最优解,所以最好去用一个全局变量去存储,然后得出最优解后再将答案跑一遍!!!*
其次我的循环操作也处理得不是很好,很多步骤的先后次序有问题,导致细节出问题
先把正常的代码贴出来吧,另一篇思一样的代码就不要看了
正常代码
#include <iostream>
#include <cmath>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int len, n, m;
int s[N];
int ans[N];
int res = 0;
int check(int mid)
{
int cnt=0;
int now=0;//巧克力序号
for(int i=1;i<=m;i++)//枚举每一天
{
cnt/=2;//先除二,昨天剩余的是否能满足今天
if(cnt>=mid) continue;
while(now<n&&cnt<mid)
{
cnt+=s[now];
ans[now++]=i;
//if(now>=n&&cnt<mid) return false;
}
if(now>=n&&cnt<mid) return false;
}
//别忘了剩下的巧克力也要吃完//又wa了一发qwq
for(;now<n;now++)
{
ans[now]=m;
}
return true;
//终于过了,太不容易了qwq qwq
}
signed main()
{
int r = 0;
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> s[i], r += s[i];
int l = 0;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (check(mid))
res = mid, l = mid;
else
r = mid - 1;
}
cout << res << endl;
check(res);
for (int i = 0; i < n; i++)
cout << ans[i] << endl;
}
不正常的代码
//我是文学家,这就是相思
//思维很混乱,万泉部诗人
#include <iostream>
#include <cmath>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int len, n, m;
int s[N];
int ans[N];
int res = 0;
int check(int mid)
{
// int tempans[51000];
int cnt = 0;
int now = 0;
for (int i = 0; i < n;)
{
cnt /= 2;
now++; ///
int j = i;
if (now > m)
{
for (; j < n; j++)
ans[j] = m;
break;
}
while (j < n && cnt < mid)
{
// cout<<1<<endl;
cnt += s[j];
ans[j] = now;
// cout<<ans[j]<<endl;
j++;
}
if (j >= n && cnt < mid)
return false;
i = j;
}
if (now < m)
{
for(int i=0;i<m-now;i++)
{
cnt/=2;
if(cnt<mid) return false;
}
}
return true;
}
signed main()
{
int r = 0;
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> s[i], r += s[i];
int l = 0;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (check(mid))
res = mid, l = mid;
else
r = mid - 1;
}
cout << res << endl;
check(res);
for (int i = 0; i < n; i++)
cout << ans[i] << endl;
}
NC235260 数字组合
链接:https://ac.nowcoder.com/acm/problem/235260
来源:牛客网
注意到数据范围为1000,首先考虑暴力加优化,枚举四层循环复杂度为O(n^4),即使将最后一层改为二分也显然超时,那么该怎么做呢
题目只要求去输出方案数,而对具体数字不做要求,那么我们是否可以去预处理一下数据(反正不需要我们输出哪些数据的和)来降低复杂度呢?
于是我们考虑先预处理出前两行和后两行数字的所有和的数量,然后二分查找相反数,则复杂度变为O(n2 + n2logn),显然满足题意
注意到需要记录前后数量,那么其实我们可以用hash存储,那么查找的复杂度降低为O(1),总体复杂度降低为O(n),注意要记录每个和的数量!!!!
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 10;//段错误
int n;
int a[N], b[N], c[N], d[N];
int l[N];
int r[N];
map<int,int>M;
signed main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
int cnt = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
l[cnt] = a[i] + b[j];
r[cnt] = c[i] + d[j];
M[r[cnt]]++;cnt++;
}
}
sort(l, l + cnt);
sort(r, r + cnt);
int ans=0;
for(int i=0;i<cnt;i++)
{
int now=-l[i];
if(M.count(now))
ans+=M[now];
}
cout<<ans<<endl;
}
NC19916 [CQOI2010]扑克牌
题目要求:1.每套牌必须包含所有不同数字,joker可看作万能牌替代任意一张。2求最多可以组成的套数
思考方向:首先考虑的是枚举答案,从大到小去考虑做多能用joker凑出多少套牌,但数据范围为5e8,会被超时,所以优化为二分枚举。
check函数根据每个数字的缺少牌数,然后得出joker的消耗数,满足两个条件–>1.小于joker数 2.小于套数(我没考虑到的,因为每套只能至多存在一张)
在check的时候可以排个序,每个数量的牌的点数并不影响判断。当我判断到牌数大于套数的时候可以直接break
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
int s[N];
int check(int mid)
{
int tempm = m;
// cout<<mid<<endl;
int cnt = 0;
for (int i = 0; i < n; i++)
{
int now = s[i];
if(s[i]>=mid) break;
int d=mid-s[i];
tempm-=d;
if(tempm<0) return false;//有可能用了多张j牌
}
if(m-tempm<=min(mid,m))//少了这一步判断,也是没考虑到的
return true;
else return false;
}
signed main()
{
cin >> n >> m;
int cnt = 0;
for (int i = 0; i < n; i++)
cin >> s[i], cnt += s[i];
sort(s, s + n);
int l = 0;
int r = cnt;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (check(mid))
l = mid;
else
r = mid - 1;
// cout<<endl;
}
cout << l << endl;
}
也有不排序直接统计的版本,思路更加清晰,有助于理解
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int n, m;
int s[N];
int check(int mid)
{
int tempm = m;
// cout<<mid<<endl;
int cnt = 0;
for (int i = 0; i < n; i++)
{
cnt += max(0ll, mid - s[i]);
}
if (cnt <= min(mid, m))
return 1; // 由于j牌每组只能用一张,所以其应该小于总组数
else
return 0;
}
signed main()
{
cin >> n >> m;
int cnt = 0;
for (int i = 0; i < n; i++)
cin >> s[i], cnt += s[i];
sort(s, s + n);
int l = 0;
int r = cnt;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (check(mid))
l = mid;
else
r = mid - 1;
// cout<<endl;
}
cout << l << endl;
}