交叉模拟(洛谷)

版权声明:转载请在原文附上源连接以及作者,谢谢~ https://blog.csdn.net/weixin_39778570/article/details/83834521

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;
} 

猜你喜欢

转载自blog.csdn.net/weixin_39778570/article/details/83834521
今日推荐