A.分豆子(贪心+思维)
题意
给你t组输入
每组输入 r (red), b(blue), d(绝对差值)
目的:
给所有的豆子打包,问是否可以全打包(YES,NO)
条件:
对于每个包,红豆的个数>=1,蓝豆的个数>=1,并且两个的个数差值<=d
思路:
贪心,以小定大,我们把每个小的都当作一个包,
然后乘上d 就可以算出所有包的含有豆子的总和,如果比Maxn要大那么肯定行,否则不行
教训:
因为这里牵扯到 int*int 没注意到 1e9的范围,WA了一次
同理还有当用 / 需要判断是否有奇偶卡判断
代码:
int main()
{
int t;
cin>>t;
while(t -- )
{
ll r,b,d;
cin>>r>>b>>d;
ll minn =min(r,b);
ll maxn =max(r,b);
if((minn)*(d+1) >= maxn)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
B.走nm找k(思维)
给你t组输入
每组输入 n ,m ,k(走的价值数 (并不是单纯的那种))
目的:
问你从1,1走到n和m能否恰好花费k个价值
条件:
每次只能往下和往右走
价值计算如下 burles(就当他是价值吧 认不到)
思路:
不管我们是怎么走,斜着走还是横竖着走,最小的方案其实都是固定不变的, 就比如你斜着走,你也是需要移动两步,所以在x和y上运动的距离其实是一样的(有点像中学物理emm)
教训:
这题虽然一遍过,但是还是A慢了,一开始以为是bfs(tm cf终于考算法了),结果不然bfs最后样例还没过(怀疑人生),结果就去看格子数(也就是价值的计算),艹了这个价值计算有诈,(优化bfs? 算了算了),继续看了会才发现是思维题
(总之cf ab题很少遇到裸的算法题就对了)
代码:
int main()
{
int t;
cin>>t;
while(t -- )
{
cin>>n>>m>>k;
if(n == 1)
{
int d = (m - 1) ;
if( d== k)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
else if(m == 1)
{
int d = (n-1) ;
if(d == k)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
else
{
int d= (n-1) ;
d+=(m-1) *(n);
if(d==k)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}
// cout<<d[1][1];
return 0;
}
C. Berland Regional(前缀和,排序,细节处理)
给你t组输入
每组输入 n ,m (表示学生i的学校,学生i的代码能力)
目的:
将每个学校分成k份(不足则不成组)求 所有学校的最大价值总和
因为学校2只有三个学生 ,所以只能分一组,因此最大价值总和28
思路:
牵扯到部分区间和,预先想到前缀和
又因为需要最大所以还需要排序
教训:
记得开longlong
code:
#include <bits/stdc++.h>
using namespace std;
const int N =2e5+10;
typedef long long ll;
vector<ll> sum[N];
vector<ll> g[N];
ll ans[N];
ll sc[N];
int n,t;
bool cmp(ll a,ll b)
{
return a>b;
}
int main()
{
cin>>t;
while(t -- )
{
cin>>n;
memset(ans,0,sizeof ans);
for(int i =1; i<=n; i++)
{
cin>>sc[i];
g[i].clear();
}
for(int j=1; j<=n; j++)
{
int x;
cin>>x;
g[sc[j]].push_back(x);
}
for(int i=1; i<=n; i++)
sort(g[i].begin(),g[i].end(),cmp);
int maxx = 0 ;
for(int i=1; i<=n; i++)
{
int len =g[i].size();
maxx = max(len,maxx);
for(int j=1; j<len; j++)
{
g[i][j] = g[i][j-1] +g[i][j];
}
}
for(int i =1; i<=n; i++)
{
int len = g[i].size();
for(int j=1; j<=len; j++)
{
ans[j] += g[i][len -1 - len%j];
}
}
for(int i=1; i<=n; i++)
cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
D.(补题 区间DP?不会,另一种老大哥做法)
题意:
给你一个n 表示数组a和b的长度
目的:
求最大的
条件:
可以对a区间做一次反转(任何区间的反转)
思路:
n = 5000 很容易想到是n2 的算法,如果单纯的枚举i,j是O(n3),所以考虑枚举顺序使得可以用到上次的结果,很容易想到以一个点为中心翻转,类似暴力枚举回文串。
code:
大佬的博客
先贴在这,上课去了
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<unordered_set>
#include<set>
#include<map>
#include<queue>
#include<vector>
using namespace std;
#define x first
#define y second
#define int long long
typedef pair<int ,int > PII;
typedef long long ll;
const int INF = 0x3f3f3f3f,mod = 1e9 + 7;
const int N = 5010;
int a[N],b[N];
int s[N];
signed main(){
int n;
scanf("%lld",&n);
for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
for(int i = 1;i <= n;i++) scanf("%lld",&b[i]),s[i] = a[i] * b[i];
for(int i = 1;i <= n;i++) s[i] += s[i - 1];
int ans = s[n];
// cout << s[n] <<endl;
for(int i = 1;i <= n;i++){
int res = s[i] - s[i - 1];
for(int len = 1;len <= n;len++){
int l = i - len,r = i + len ;
if(l <= 0 || r > n) break;
res += a[l] * b[r] + a[r] * b[l];
int t = s[n] - (s[r] - s[l - 1]);
ans = max(ans,res + t);
}
res = 0;
for(int len = 1;len <= n;len++){
int l = i - len + 1,r = i + len ;
if(l <= 0 || r > n) break;
res += a[l] * b[r] + a[r] * b[l];
int t = s[n] - (s[r] - s[l - 1]);
ans = max(ans,res + t);
}
}
cout << ans << endl;
return 0;
}