1. 程式人生 > >交叉模擬(洛谷)

交叉模擬(洛谷)

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