ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
P1023 税收与补贴问题
题目:https://www.luogu.org/problemnew/show/P1023
题意:求(价格-本金+补贴)x 数量 = 利润 这个值当价格为预算时最大,也就是说找到一个补贴值,使得价格为政府预算时,利润最大
解法:
上式 为 价格x数量 - 本金x数量 + 补贴x数量 = f(x,y) 当补贴和本金是常数时 f(x) = xy + Cy; x升y减 , f(x)是一个单峰函数???不会证明
当补贴一定时,价格和数量是变量,他们是绑定一起的,如果有解的情况, 上式可看做是一个单峰函数
自变量为 (价格,数量) 值为 利润, 当自变量为 (预算,预算销量) 时 利润最大 即峰值
那么我们只需要计算 3 个自变量 (预算-1,此时的销量) (预算,此时的销量) (预算+1,此时的销量)
再枚举 补贴, 当 (预算,此时的销量) 所对应的利润为峰值时补贴即为答案
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int a[100010]; // a[i] 表示 售价i 销量a[i]
int yusuan,chengben,max_val;
int d_first,d_second; // 第一个等差值,第二个等差值 (绝对值)
void get_num(int yusuan){ // 计算销量
if(!a[yusuan]){
if(yusuan>max_val) a[yusuan] = a[max_val] - d_second*(yusuan-max_val);
else a[yusuan] = a[max_val] + d_first*(max_val-yusuan);
}
}
int main(){
scanf("%d",&yusuan);
scanf("%d",&chengben);
scanf("%d",&a[chengben]);
int x,y;
max_val=chengben;
int f = 1; // 标记第一个而已
while(scanf("%d%d",&x,&y) && (x!=-1||y!=-1)){
a[x] = y;
max_val = max(max_val,x);
if(f==1){
d_first = (a[chengben]-a[x])/(x-chengben); // 这是递减的 不要减反了...
f=0;
}
}
scanf("%d",&d_second);
get_num(yusuan-1);
get_num(yusuan);
get_num(yusuan+1);
// 若有多解,取绝对值最小的输出。所以从开始枚举
ll x1,x2,x3;
for(int butie = 0; butie<=100000; butie++){
x1 = (yusuan-1-chengben+butie)*a[yusuan-1];
x2 = (yusuan-chengben+butie)*a[yusuan];
x3 = (yusuan+1-chengben+butie)*a[yusuan+1];
if(x2>=x1 && x2>=x3){
printf("%d",butie);
return 0;
}
x1 = (yusuan-1-chengben-butie)*a[yusuan-1];
x2 = (yusuan-chengben-butie)*a[yusuan];
x3 = (yusuan+1-chengben-butie)*a[yusuan+1];
if(x2>=x1 && x2>=x3){
printf("%d",-butie);
return 0;
}
}
// 无解
puts("NO SOLUTION");
return 0;
}
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<bits/stdc++.h>
#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);
}
P1042 乒乓球
题目:https://www.luogu.org/problemnew/show/P1042
题意:比如现在有这么一份记录,(其中W表示华华获得一分,L表示华华对手获得一分):WWWWWWWWWWWWWWWWWWWWWWLW
在11分制下,此时比赛的结果是华华第一局11比0获胜,第二局11比0获胜,正在进行第三局,当前比分1比1。而在21分制下,此时比赛结果是华华第一局21比0获胜,正在进行第二局,比分2比1。如果一局比赛刚开始,则此时比分为0比0。直到分差大于或者等于2,才一局结束。
解法:很简单的模拟 字符输入,不确定换行,用cin,很好用
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
vector<pair<int,int> >ans1,ans2;
int W11,L11,W21,L21;
int main(){
char c;
while(cin>>c){
if(c=='E'){
ans1.push_back(make_pair(W11,L11));
ans2.push_back(make_pair(W21,L21));
break;
}
if(c=='W'){
W11++;W21++;
}else if(c=='L'){
L11++;L21++;
}
if(W11>=11 || L11>=11){
if(abs(W11-L11)>=2){
ans1.push_back(make_pair(W11,L11));
W11=L11=0;
}
}
if(W21>=21 || L21>=21){
if(abs(W21-L21)>=2){
ans2.push_back(make_pair(W21,L21));
W21=L21=0;
}
}
}
for(pair<int,int> p : ans1){
cout<<p.first<<":"<<p.second<<endl;
}
cout<<endl;
for(pair<int,int> p : ans2){
cout<<p.first<<":"<<p.second<<endl;
}
return 0;
}
P1086 花生采摘
题目:https://www.luogu.org/problemnew/show/P1086
题意:采摘花生的顺序必须从大到小,从路边出发,且能回到路边,采摘时花费时间1,走路1步也花费1,求规定时间内能采摘的花生最多为多少
解法:直接模拟
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
struct node{
int x,y,val;
bool operator < (const node &a)const{
return val>a.val;
}
};
int n,m,k;
vector<node> ver;
// 计算曼哈顿距离
int calc(node a, node b){
return fabs(a.x-b.x) + fabs(a.y-b.y);
}
int main(){
scanf("%d%d%d",&n,&m,&k);
fo(i,1,n)fo(j,1,m){
node x;
scanf("%d",&x.val);
x.x=i;x.y=j;
ver.push_back(x);
}
sort(ver.begin(),ver.end()); // 从大到小排序一下
int ans = 0;
node now;
for(int i=0; i<ver.size(); ++i){
if(i==0){
if(k>=2*ver[i].x+1){ // 来回 + 采摘
ans+=ver[i].val;
now = ver[i];
k-=(ver[i].x+1);
}else break; // 一定要记得 break 了 Wa啊...
}else{
int t = calc(now,ver[i]);
if(k>= t + 1 +ver[i].x){ // 上一个到下一个的距离 + 采摘时间 + 下一个能回到路边
ans+=ver[i].val;
now = ver[i];
k-=(t+1);
}else{
break;
}
}
}
cout<<ans;
}
P1098 字符串的展开
题目:https://www.luogu.org/problemnew/show/P1098
题意:怎么理解题意很重要,简而言之,判断一个’-‘是否是可以替换的,可以就替换了。
解法:一开始我是判断 字符-字符 这样的结构是不是能替换的,能就整个替换了,结果特殊情况很多,所以还是判断’-’,替换’-'好了…
乱七八糟的情况好多
如下:
a-b-c 连续符合情况的
–s-- 头尾可能为-
s–a 两个–出现
1-a 数字和字母
其实仔细想想我们只要考虑 - 这种情况就好了,如果这个-是可以替换的就替换掉,否则不替换
解法一:别人的
/* 这个代码是真的牛逼 大致就是把可以替换的'-'替换掉,其他不变 */
#include<bits/stdc++.h>
using namespace std;
int p1,p2,p3,i=0,k;
char ch[300],be,af,f,j,p;//p用于输出;
int main() {
scanf("%d%d%d%s",&p1,&p2,&p3,ch);//输入;
while(ch[i]){//当ch[i]有值时;
be=ch[i-1];af=ch[i+1];f=ch[i];//f存储ch[i],便于判断;
if(f=='-'&&af>be&&(be>='0'&&af<='9'||be>='a'&&af<='z')){//意思是ch[i]若为'-',就判断其前后是否满足条件,满足进入循环;
for(p3==1?j=be+1:j=af-1; p3==1?j<af:j>be; p3==1?j++:j--){
p=j;//j是整形变量,p是字符型变量,这样是将p赋值为ASCII码为j的字符;
if(p1==2)//是否大写;
p=(p>='a')?p-32:p;//如果是字母就转成大写
else if(p1==3) p='*';//是否输出'*'
for(k=0; k<p2; k++)//输出p2个
printf("%c",p);
}
}
else
printf("%c",f);//如果ch[i]是非'-'或者其前后不满足条件,就原样输出;
i++;//一定要放在后面,不然会出错QAQ;
}
return 0;
}
沙雕版本
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int p1,p2,p3;
string s;
char upchar(char c){
return c+='A'-'a';
}
void solve(){
string ss;
int n = s.length();
for(int i=0; i<n; i++){
// 枚举所有能替换的情况
if(i-1>=0 &&i+1<n && s[i]=='-' && s[i-1]<s[i+1] && (s[i-1]>='a'&&s[i+1]<='z' || s[i-1]>='0' && s[i+1]<='9')){
// 能填充的情况 ,需要同是字母或同是数组
int t = s[i+1]-s[i-1]-1;
if(p1==3){ // 填p2个*
fo(a,1,t)fo(b,1,p2)ss+='*';
}else if(p1==1){ // 小写
char c=s[i-1];
if(p3==1){ // 正序
for(int a=1; a<=t; a++){
fo(b,1,p2) ss+=(c+a); // 填p2个
}
}else if(p3==2){ // 逆序
for(int a=t; a>=1; a--){
fo(b,1,p2) ss+=(c+a); // 填p2个
}
}
}else if(p1==2){ // 大写要排除数字的情况
char c=s[i-1];
if(p3==1){
for(int a=1; a<=t; a++){ // 正序
if(isalpha(c+a))fo(b,1,p2)ss+=upchar(c+a); // 填p2个
else fo(b,1,p2) ss+=(c+a);
}
}else if(p3==2){
for(int a=t; a>=1; a--){ // 逆序
if(isalpha(c+a)) fo(b,1,p2) ss+=upchar(c+a); // 填p2个大写字母
else fo(b,1,p2) ss+=(c+a);
}
}
}
}else{
ss+=s[i]; // 不能替换
}
}
cout<<ss;
}
int main(){
scanf("%d%d%d",&p1,&p2,&p3);
cin>>s;
solve();
return 0;
}