*PS:爲什麼我t5死活都調不出來呢(눈‸눈)
T1:原創,參考noip火柴排隊。
T2: codevs 2178 表達式運算Cuties (我自己找的)
T3:Codeforce867E(865D) Buy Low Sell High
T4: USACO 2015 DEC Gold 不明鏈接1 不明鏈接2
( 該網站不提供在線測評,僅提供數據下載。考試使用原數據。)
T5:原創。
T1 原創 火柴排隊的改編版
一說到火柴排隊,應該就會做了。(好巧 這個題我前些天才看過qwq)。
解法:
對於B中的每個字符都統計下他在A中出現的位置,存入一個數組裏,這個位置就是它最後要移到的位置。最後對這個數組求一個逆序對,逆序對的數量就是最少需移動的步數。
不能的情況就是B和A的字符種類或數量不同,輸入時處理。
代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int N,ans,cnt,Cnt;
int a[110],used[1000],b[110];
char c[110];
struct maple{
int pos;
char a;
}A[110],B[110];
bool cmp(maple c,maple d)
{
return c.a<d.a;
}
void hebing(int l,int mid,int r)
{
int i=l,j=mid+1,k=l-1;
while(i<=mid&&j<=r)
{
if(a[i]<=a[j]) b[++k]=a[i],++i;
else b[++k]=a[j],ans+=mid-i+1,++j;
}
while(i<=mid) b[++k]=a[i],++i;
while(j<=r) b[++k]=a[j],++j;
for(int i=l;i<=r;++i)
a[i]=b[i];
}
void Mergesort(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
Mergesort(l,mid);
Mergesort(mid+1,r);
hebing(l,mid,r);
}
int main()
{
freopen("order.in","r",stdin);
freopen("order.out","w",stdout);
scanf("%d",&N);
scanf("%s",c);
for(int i=0;i<N;++i)
{
A[i].a=c[i],A[i].pos=i;
if(!used[c[i]]) ++cnt;
++used[c[i]];
}
scanf("%s",c);
for(int j=0;j<N;++j)
{
B[j].a=c[j],B[j].pos=j;
--used[c[j]];
if(!used[c[j]]) ++Cnt;
if(used[c[j]]<0) Cnt=-110;
}
if(cnt==Cnt)
{
sort(A,A+N,cmp);
sort(B,B+N,cmp);
for(int i=0;i<N;++i)
a[B[i].pos]=A[i].pos;
Mergesort(0,N-1);
cout<<ans;
}
else cout<<-1;
return 0;
}
T2 簡單的棧應用
注意處理負數的情況,判斷棧空的函數一定要放在最前面。
代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
int k[100],f=1;
char a[210];
stack<long long> num;
stack<char> opt;
void Done()
{
char x=opt.top();
opt.pop();
long long a=num.top();num.pop();
long long b=num.top();num.pop();
long long c=0;
if(x=='+') c=a+b;
if(x=='-') c=b-a;
if(x=='*') c=b*a;
if(x=='/') c=b/a;
if(x=='^')
{
c=1;
while(a)
{
c*=b;
--a;
}
}
num.push(c);
}
int main()
{
freopen("formula.in","r",stdin);
freopen("formula.out","w",stdout);
scanf("%s",a);
k['+']=k['-']=1;
k['*']=k['/']=2;
k['^']=3;
k['(']=4;
num.push(0);
long long l=strlen(a),Num=0;
a[l]=')';
for(int i=0;i<=l;++i)
{
if(a[i]>='0'&&a[i]<='9') Num=Num*10+(a[i]-'0'),Num*=f,f=1;
else{
if(Num!=0) num.push(Num);
Num=0;
if(a[i]=='-'&&a[i-1]=='(')
{
f=-1;
continue;
}
if(a[i]==')')
{
while(!opt.empty()&&opt.top()!='(') Done();
if(!opt.empty()) opt.pop();
}
else if(opt.empty()||opt.top()=='('||k[a[i]]>k[opt.top()]) opt.push(a[i]);
else
{
while(!opt.empty()&&opt.top()!='('&&k[a[i]]<=k[opt.top()]) Done();
opt.push(a[i]);
}
}
}
while(!opt.empty()&&opt.top()!='(') Done();
cout<<num.top();
return 0;
}
T3 看似DP的貪心
是不是很像之前的一道有N天每天可買可賣可不作處理的dp問題??
但是那個題有個限制就是每天手裏最多持有一份股票,而這個題則是多份的。
因爲這個題是t3而之前又做過類似的題,所以我馬上就在想dp的路上一去不復返了QAQ。。。
最後確實打出來了,測的時候卻顯示MLE!!!因爲long long的數組開的太大!!! 好吧。。。最後知道long long 在128MB下也就能開個1600萬+。。。
定義 f[i][j]表示到第i天手裏有j份股票所賺到的最多的錢數
可知
f[i][j]=max(f[i][j],f[i-1][j]); // 不作處理
f[i][j]=max(f[i][j],f[i-1][j+1]+P[i]); // 賣出
f[i][j]=max(f[i][j],f[i-1][j-1]-P[i]); //買入
改完後可以過5個點
DP代碼
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const int Cn=300000+10;
int N;
long long P[Cn],f[4000][4000];
int main()
{
freopen("resell.in","r",stdin);
freopen("resell.out","w",stdout);
scanf("%d",&N);
for(int i=1;i<=N;++i)
scanf("%lld",&P[i]);
for(int i=1;i<=N;++i)
for(int j=0;j<=N;++j)
{
f[i][j]=-1e9+7;
if(j<=i)
{
if(i!=1)
{
f[i][j]=max(f[i][j],f[i-1][j]);
f[i][j]=max(f[i][j],f[i-1][j+1]+P[i]);
if(j-1>=0) f[i][j]=max(f[i][j],f[i-1][j-1]-P[i]);
}
else f[1][0]=0,f[1][1]=-P[1];
}
}
cout<<f[N][0];
return 0;
}
emmmm。。。如果用滾動數組優化的話可以過6個點,就是另開一個數組先記錄一下f[i-1]的值用來更新。。。這裏就不給出代碼了。
正解 ·貪心
來自loi.Sherlock的題解總結(就是講題時我做的筆記啊qwq,略長)
emmm...要賺的錢最多,我們肯定是低價買入,高價買出。。
大體上想想:可以維護一個小根堆,每過一天,用當天的價格和堆頂比較,如果價格大於堆頂,ans就加上*當天價格-堆頂價格*,彈出堆頂;如果小於等於,就加入堆。
但是這個做法有一個bug,舉個栗子:
2 5 10
按照我們現在的貪心做法,答案應該是 5-2=3 (每天只能操作一次),但是正確答案應該是 10-2=8
這就很尷尬了。。。(劃掉)
這時我們發現 10-2=(10-5)+(5-2) ,這是不是代表我可以利用一下5呢??
對於 a<b<c 的情況,我要利用中介b ,就把b放一個進去(之前是b>a時,只統計ans,b並沒有被放入)用來做中介。
但是這樣還有一個bug
如 2 5 10 7 輸出 10-5+5-2=8 ,但正確答案是 10-2+7-5=10
我們發現之前的5實際上並沒有被用到,反而被彈出了。
解決的方法就是一開始就放上兩個5,一個做中介,一個用來更新答案。
所以對之前貪心做法的改進就是當我遇到一個比堆頂大的元素時,更新ans,彈出堆頂,同時放入兩個當前價格,對答案不會有影響。
代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int N;
long long ans=0;
int P[300010];
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
freopen("resell.in","r",stdin);
freopen("resell.out","w",stdout);
scanf("%d",&N);
for(int i=1;i<=N;++i)
{
scanf("%d",&P[i]);
if(!q.empty()&&P[i]>q.top())
{
ans+=P[i]-q.top();
q.pop();
q.push(P[i]);
}
q.push(P[i]);
}
printf("%lld",ans);
return 0;
}
T4 無比麻煩的BFS
注意特判啊!!我做的時候WA,TLE,MLE真是夠了!
注意紫色的格子上的移動方向是與之前進入的方向有關的,並不是可以隨便走的!! 所以要另開一維記錄前進的方向,但是並不是每個格子的前進方向都要記錄,只需記錄紫色格子的,不然會TLE。。
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int mxn=1000+5;
int N,M,f;
int x[4]={0,1,0,-1}, // 0--> 右 1--> 下 2--> 左 3-->上
y[4]={1,0,-1,0};
int A[mxn][mxn];
int used[mxn][mxn][2][5];
struct maple{
int x,y,z,face,d;
};
queue<maple>q; // 手打隊列會mle
bool can(int k,maple a,int K)
{
int X=a.x+x[k],Y=a.y+y[k];
if(X<=0||X>N||Y<=0||Y>M||used[X][Y][a.z][K]) return false;
if(A[X][Y]==0) return false;
if(A[X][Y]==3&&!a.z) return false;
return true;
}
void Bfs()
{
q.push((maple){1,1,0,0,0});
used[1][1][0][4]=1;
while(!q.empty())
{
maple a=q.front();
q.pop();
if(a.x==N&&a.y==M)
{
f=1;
printf("%d",a.d);
return ;
}
if(A[a.x][a.y]==4)
{
int face=a.face;
int X=a.x+x[a.face],Y=a.y+y[a.face];
if(A[X][Y]!=4) face=4; // 記錄每個格子的方向會T
if(used[X][Y][a.z][face]) continue;
if(can(a.face,a,face))
{
int xx=a.x+x[a.face],yy=a.y+y[a.face],z=a.z,d=a.d+1;
if(A[xx][yy]==2) z=1,used[xx][yy][0][face]=1; // 不加會T 1
q.push((maple){xx,yy,z,face,d});
used[xx][yy][z][face]=1;
continue;
}
}
for(int i=0;i<4;++i)
{
int xx=a.x+x[i],yy=a.y+y[i],face=i;
if(A[xx][yy]!=4) face=4;
if(can(i,a,face))
{
int z=a.z,d=a.d+1;
if(A[xx][yy]==4) z=0;
if(A[xx][yy]==2) z=1,used[xx][yy][0][face]=1; //不加會T 2
q.push((maple){xx,yy,z,face,d});
used[xx][yy][z][face]=1;
}
}
}
}
int main()
{
freopen("cowboy.in","r",stdin);
freopen("cowboy.out","w",stdout);
scanf("%d%d",&N,&M);
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
scanf("%d",&A[i][j]);
Bfs();
if(!f) cout<<-1;
return 0;
}
//*PS:註釋的都是血的教訓。。。QAQ
T5 十分明顯的exgcd??
再次%%%原創題dalao @loi.MurasameKatana
由於這個題我還是沒能調出來,所以我打算十分不要臉的打算粘上題解和std_(:з」∠)_ 。。。。ε=ε=ε=┌(; ・_・)┘跑
而對於本題,首先我們將起點到所有城鎮的距離(最短路)排序,每次選取存在妖刀且路程之和爲當前最小的兩個城鎮中的妖刀,取其攻擊力爲a b,結界初始防禦值-臨界防禦值爲c,那麼兩把妖刀各自的使用次數爲方程的解x,y
那麼我們首先通過exgcd()求出一組解滿足ax + by = gcd(a,b)
注:exgcd()求出的特解爲 |x| + |y| 最小的一組解
然後得到滿足ax + by = c的解,再推導其他的解找到:
兩個使用次數大於0,使用方案不會使任意一把妖刀折斷,且消耗的磨損值之和最小的解。
這便是T5朧村正的算法過程
然後就是注意細節處理
by Loi_MurasameKatana
(侵刪)
Std
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define RI register int
using namespace std;
typedef long long ll;
const int INF =1000000007;
ll n,m,h,p,ru,rv,rw,tot,x,y,xs,ys,ans,anz,A,Z,minx,f;
ll dis[100010],first[200010],nxt[200010],c[100010],r[100010],k[100010],t[100010],num[100010];
bool flag;
bool inq[100010],unb[100010];
struct edge
{
ll u,v,w;
}l[400010];
queue<int>q;
inline bool cmp(ll a,ll b)
{
if(dis[a]!=dis[b])
return dis[a]<dis[b];
else return a<b;
}
inline void build(ll f,ll t,ll c)
{
l[++tot]=(edge){f,t,c};
nxt[tot]=first[f];
first[f]=tot;
}
inline bool check_1(ll a,ll b,ll c)//分情況討論邊界條件
{
if(a>0&&b>0)
{
if(xs<=0||ys>(k[num[c]]/r[num[c]]))
return 0;
}
else if(a>0&&b<0)
{
if(xs>(k[num[c-1]]/r[num[c-1]])||ys>(k[num[c]]/r[num[c]]))
return 0;
}
else if(a<0&&b>0)
{
if(xs<=0||ys<=0)
return 0;
}
else if(a<0&&b<0)
{
if(xs>(k[num[c]]/r[num[c]])||ys<=0)
return 0;
}
return 1;
}
inline bool check_2(ll a,ll b,ll c)
{
if(a>0&&b>0)
{
if(xs>(k[num[c-1]]/r[num[c-1]])||ys<=0)
return 0;
}
else if(a>0&&b<0)
{
if(xs<=0||ys<=0)
return 0;
}
else if(a<0&&b>0)
{
if(xs>(k[num[c-1]]/r[num[c-1]])||ys>(k[num[c]]/r[num[c]]))
return 0;
}
else if(a<0&&b<0)
{
if(xs<=0||ys>(k[num[c]]/r[num[c]]))
return 0;
}
return 1;
}
inline void SPFA()
{
for(RI i=1;i<=n;i++)
dis[i]=INF;
dis[1]=0;
q.push(1);
inq[1]=1;
while(!q.empty())
{
int k=q.front();
q.pop();
inq[k]=0;
for(RI i=first[k];i!=-1;i=nxt[i])
{
ll x=l[i].v;
if(dis[x]>dis[k]+l[i].w)
{
dis[x]=dis[k]+l[i].w;
if(!inq[x])
{
q.push(x);
inq[x]=1;
}
}
}
}
}
inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll d=exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-a/b*y;
return d;
}
int main()
{
freopen("blade.in","r",stdin);
freopen("blade.out","w",stdout);
memset(first,-1,sizeof(first));
scanf("%lld%lld%lld%lld",&n,&m,&h,&p);
for(RI i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&ru,&rv,&rw);
build(ru,rv,rw);
build(rv,ru,rw);
}
for(RI i=1;i<=n;i++)
{
scanf("%lld%lld%lld%lld",&c[i],&r[i],&k[i],&t[i]);
if(t[i]==1)
c[i]*=-1;
if(!c[i]&&!r[i]&&!k[i]&&!t[i])
unb[i]=1;
}
SPFA();
for(RI i=1;i<=n;i++)
if(unb[i]==1)
dis[i]=INF;
for(RI i=1;i<=n;i++)
num[i]=i;
sort(num+1,num+n+1,cmp); // 給城鎮排序
minx=1e9+7;
for(RI i=2;i<=n;i+=2)
{
if(unb[num[i-1]]||unb[num[i]]) // 若搜遍周邊城鎮也沒有找到
break;
ll d=exgcd(c[num[i-1]],c[num[i]],x,y);//ax+by=gcd(a,b)
if(!d||(h-p)%d!=0) //%0RE 若無解
continue;
ll s=(h-p)/d;//asx+bsy=c
x*=s;
y*=s;
if(x<y)
{
swap(x,y);
swap(c[num[i-1]],c[num[i]]);
swap(r[num[i-1]],r[num[i]]);
swap(k[num[i-1]],k[num[i]]);
}
int li=x;
int lr=y;
for(RI j=0;;j++)//j>=0
{
xs=x-(c[num[i]]/d)*j;//xs=x-b/d*j
ys=y+(c[num[i-1]]/d)*j;//ys=y+a/d*j
if(!check_1(c[num[i-1]]/d,c[num[i]]/d,i))
break;
if(xs>0&&ys>0&&xs<=(k[num[i-1]]/r[num[i-1]])&&ys<=(k[num[i]]/r[num[i]]))//合法情況,使用次數大於0且未達次數上限
{
if(((xs*r[num[i-1]]+ys*r[num[i]])<minx)||(A!=0&&Z!=0&&(xs*r[num[i-1]]+ys*r[num[i]]==minx)&&(abs(xs*r[num[i-1]]-ys*r[num[i]])<abs(A*r[num[i-1]]-Z*r[num[i]]))))//取磨損值消耗之和最小的方案
{//若存在多解取磨損值相差最小的
minx=(xs*r[num[i-1]]+ys*r[num[i]]);
A=xs;
Z=ys;
flag=1;//存在合法解
}
}
}
x=li;
y=lr;
for(RI j=-1;;j--)//j<0
{
xs=x-(c[num[i]]/d)*j;
ys=y+(c[num[i-1]]/d)*j;//x爲大正數,y爲大負數的情況不會導致非法退出
if(!check_2(c[num[i-1]]/d,c[num[i]]/d,i))
break;
if(xs>0&&ys>0&&xs<=(k[num[i-1]]/r[num[i-1]])&&ys<=(k[num[i]]/r[num[i]]))
{
if(((xs*r[num[i-1]]+ys*r[num[i]])<minx)||(A!=0&&Z!=0&&(xs*r[num[i-1]]+ys*r[num[i]]==minx)&&(abs(xs*r[num[i-1]]-ys*r[num[i]])<abs(A*r[num[i-1]]-Z*r[num[i]]))))
{
minx=(xs*r[num[i-1]]+ys*r[num[i]]);
A=xs;
Z=ys;
flag=1;
}
}
}
if(flag)
{
ans=min(A*r[num[i-1]],Z*r[num[i]]);//統計
anz=max(A*r[num[i-1]],Z*r[num[i]]);
break;
}
}
if(flag)
{
printf("%lld %lld\n",ans,anz);
}
else printf("Game over\n");
return 0;
}
The end.