交叉模擬(洛谷)
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