ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
P1031 均分纸牌
题目链接:https://www.luogu.org/problemnew/show/P1031
解法一:直接模拟(能求出过程)
解法二,贪心
A[i]为减去a[i]平均数的值
sum[i]为A前缀和
从做至右当sum[i]<0时说明第i位不够,需要i+1位左移动abs(sum[i])张,步数加1
当sum[i]>0时说明第i位多了,需要向i+1位左移动abs(sum[i])张,步数加1
此时i已经到达平均数了,从左至右依次执行操作,i的左边都是已经到达平均数的,i是需要调整的,i的右边是用来调整i的
当A[i+1]往左移动不够abs(sum[i])张时候,可以理解为先向后面的借了不够的张数
借的张数会在计算i+1这一位sum[i+1]体现出来及时借过来,向i+2借(虽然i+2也可能时借的),但往后面总有一个是不用借的
#include
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,a[200],sum[200];
// 模拟过程
void solve(){
fo(i,1,n)sum[i]=sum[i-1]+a[i];
int ave = sum[n]/n;
int count=0,d;
for(int i=1; i<=n; i++)
{
if(sum[i]>i*ave) //sumstd[i]=i*ave, 向后面均匀一些
{
d = sum[i]-i*ave;
sum[i] -= d;
a[i] -= d;
a[i+1] += d;
count++;
}
}
for(int i=n; i>=1; i--)
{
if(a[i]>ave) //向前面均匀一些
{
d = a[i]-ave;
a[i] -= d;
a[i-1] += d;
sum[i-1] += d;
count++;
}
}
cout << count;
}
int main(){
scanf("%d",&n);
int all=0;
fo(i,1,n){
scanf("%d",&a[i]);
all+=a[i];
}
int k = all/n, ans=0;
fo(i,1,n){
sum[i] = sum[i-1] + a[i]-k;
ans += abs(sum[i])==0?0:1;// i左边已经平均,即都为0,查看sum[i]是否多了或者少了,多了往右移动,少了右边往左移动
}
printf("%d\n",ans);
}
P1233 木棍加工
题目链接:https://www.luogu.org/problemnew/show/P1233
法一:贪心,在宽度排序好的情况下,下降排序 ,求最少有多少个长度递减序列
法二:DP,如果是求下降子序列的最小划分,相当于是求最小反链划分,等于最长不下降子序列的长度。 最长严格上升序列长度
最长严格上升序列长度 ,DP做法(慢),二分做法(nlogn快,每次查找出新数组1~len范围内大于等于要插如的数的下标,然后插入
#include
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
struct Node{
int l,w;
bool operator < (const Node & x)const{
if(w==x.w){
return l>=x.l;
}else return w>=x.w;
}
}a[5005];
int n;
bool vis[5050];
// 40ms 贪心扫ans遍
void solve(){
sort(a+1,a+1+n); // 宽度已经排序好,下降排序
// fo(i,1,n){
// printf("%d %d\n",a[i].l,a[i].w);
// }
int ans = 0;
int l;
while(1){
bool flag=0;
fo(i,1,n)if(!vis[i]){
l = a[i].l; // 找到第一个未标记的
flag=1;
break;
}
if(!flag)break; // 都标记过了
fo(i,1,n){
if(!vis[i]&&a[i].l<=l){ // 在宽度递增减长度递增排序的情况下,对长度进行贪心选择
l = a[i].l;
vis[i]=1;
}
}
ans++;
}
printf("%d\n",ans);
}
// DP n^2 180ms
int dp[5050];
void solve1(){
sort(a+1,a+1+n); // 宽度已经排序好,下降排序
int ans = 0;
fo(i,1,n){
dp[i]=1;
fo(j,1,i){
if(a[i].l>a[j].l && dp[i]<dp[j]+1){ //求最长严格上升序列长度
dp[i] = dp[j]+1;
}
}
if(ans<dp[i])ans=dp[i];
}
printf("%d\n",ans);
}
// nlogn求最长严格上升序列 33 ms
//用二分的方法在 b[] 数组中找出第一个大于等于 a[i] 的位置并且让a[i] 替代这个位置
int binary_search(int a[], int l, int r, int x){
int ans = 1,mid;
while(l<=r){
mid = (l+r)>>1;
// cout<
if(a[mid]>=x){
ans = mid;
r = mid -1;
}else{
l = mid +1;
}
}
return ans; // 返回大于等于x的第一个下标
}
int b[5050];
bool cmp(const Node &a, const Node &b){ return a.w>b.w;}
void solve2(){
sort(a+1,a+1+n); // 宽度已经排序好,下降排序
int i,len,pos;
b[1]=a[1].l;
len=1;
for(i=2;i<=n;i++){
if(a[i].l>b[len]){
b[++len] = a[i].l;
}else{
pos = binary_search(b,1,len,a[i].l); // 返回大于等于a[i].l的第一个下标
// b[pos] = min(b[pos],a[i].l); // 范围一定要为 1,len, 傻傻写了无数遍1-n错了
b[pos] = a[i].l;
}
}
printf("%d\n",len);
}
int main(){
// int c[] = {1,2,3,4,4,5,6,6,6,6,7,10,12};
// cout<
scanf("%d",&n);
fo(i,1,n){
scanf("%d%d",&a[i].l,&a[i].w);
}
//solve();
//solve1();
solve2();
}
P1080 国王游戏
题目链接:https://www.luogu.org/problemnew/show/P1080
解法:相邻两项交换位置对后面的前面和后面没有影响
对左手和有右手乘积进行递增排序,求出过程中最大的结果,证明略
#include
#include
#include
#include
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int N = 1e5+5;
struct minister{
int l,r,rank;
minister(){}
minister(int a,int b):l(a),r(b),rank(a*b){}
bool operator <(const minister &x)const{ return rank<x.rank; }
}a[N];
struct BigInt{
int data[N];
BigInt(){
memset(data, 0, sizeof(data));
data[0]=1;
}
BigInt(int x){//用低精数 初始化(赋值) 高精数
memset(data, 0, sizeof(data));
data[0] = 1;
int i=1;
while(x){
data[i++] = x%10;
x/=10;
}
data[0]=--i;// 这里位数不能错
}
BigInt operator *(const int &x){
BigInt a;
int len;
a.data[0] = data[0];
fo(i,1,data[0])a.data[i] = data[i]*x;
for(int i=1; i<=a.data[0]||a.data[i]; len = ++i){
a.data[i+1] += a.data[i]/10;
a.data[i] %= 10;
}
a.data[len] ? a.data[0] = len : a.data[0] = --len;
return a;
}
BigInt operator /(const int &x){
BigInt a;
a.data[0]=data[0];
int rest = 0;
for(int i=data[0]; i>=1; i--){ // 注意从高位开始模拟
rest = rest*10+data[i];
a.data[i] = rest/x;
rest %= x;
}
while(!a.data[a.data[0]] && a.data[0]>1) a.data[0]--;// 去掉多余的0
return a;
}
bool operator <(const BigInt &x)const{
if(data[0]==x.data[0]){ // 先比较位数
int i;
for(i=data[0]; data[i]==x.data[i] && i>1; i--); // 再比较高位
if(i>=1) return data[i]<x.data[i];
else return false;
}
else return data[0]<x.data[0];
}
};
ostream& operator << (ostream &out, const BigInt &x){
fo(i,1,x.data[0]) out << x.data[x.data[0]-i+1];
return out;
}
int n,x,y;
BigInt ans;
int main(){
ios::sync_with_stdio(false);
cin>>n;
fo(i,0,n){
cin>>x>>y;
a[i] = minister(x,y);
}
sort(a+1,a+1+n);
BigInt k(1);
BigInt temp;
fo(i,1,n){
if(a[i-1].l==0)break; // 特判0
k = k*a[i-1].l;
// 注意被除数用temp 存下来
//如有大臣 防止商为0 后 k无法更新
temp = k/a[i].r;
if(ans<temp)ans=temp;
}
cout<<ans<<endl;
return 0;
}
P2123 皇后游戏
题目链接:https://www.luogu.org/problemnew/show/P2123
题解:https://blog.csdn.net/liuzibujian/article/details/81435356
#include
#include
#include
using namespace std;
struct node
{
int x,y,d;
bool operator <(node a) const
{
if (d!=a.d) return d<a.d; // 两者的左右关系不同时,先比较d
if (d<=0) return x<a.x; // 本节点左手小于等于右手时,按左手升序
return y>a.y; // 否则按右手降序
}
}a[20005];
int t,n;
long long c[20005];
int main()
{
cin>>t;
for (int k=1;k<=t;k++)
{
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y;
if (a[i].x>a[i].y) a[i].d=1; // 左大于右边
else if (a[i].x<a[i].y) a[i].d=-1; // 左小于右
else a[i].d=0;
}
sort(a+1,a+n+1);
long long s=0;
for (int i=1;i<=n;i++)
{
s+=a[i].x;
c[i]=max(c[i-1],s)+a[i].y;
}
cout<<c[n]<<'\n';
}
}