C# 9.0 中的新增功能
太菜了,卡在C上以至於沒有看到D。
A. Marketing Scheme
題意:如果 x mod a ≥⌊a/2⌋,那麼這是好的。問在x∈[l,r]時,能否找到一個a,使得對於區間內任意的x都是好的。
對於區間左端點,a能取得的最大的情況,就是a=2*l了,那麼只要r小於2*l就成立。
B. Reverse Binary Strings
題意:翻轉01串,使之變成01交題串
兩種理解:
1、翻轉一次不會造成內部的變化,只改變首尾兩點。那麼我們找多少個需要被交換的0或者1就行了。
比如(111)那麼就有兩個1需要被翻轉走,需要被交換的01個數取大就好。
int cnt1 = 0, cnt2 = 0; for(int i=1;i<s.length();++i)( { if(s[i]==s[i-1]) { if(s[i]=='1') cnt1++; else cnt2++; } } cout<<max(cnt2,cnt1)<<endl;
2、目標串只有101010以及010101的形式,所以,既然翻轉一次內部方向改變,內部交替不變,那麼我再換一次內部就好了。
比如11101000的目標是10101010,那麼只有2和6號位不匹配,那麼我翻轉2-6,再翻轉3-5就能達成目標。
對於目標是101010,但當中的四位0101都不匹配,那麼我只用翻轉一次就好。
這時候就會有一個問題?當前串01010不匹配呢,翻轉也不行的啊?考慮到其他地方會多一個1不匹配,所以不計也沒關係。
那麼如果一定要多次操作才能還原後面的1呢?那這時候目標串是010101不是更好嗎。
證明不是很嚴謹,當時是看到隊友a了,急中生智,看了一下跟別人的解法都不一樣。
可能是錯的。
memset(vis,0,sizeof(vis)); ans=anss=0; scanf("%d",&n); scanf("%s",a+1); //10101010 f=1;for(int i=1;i<=n;++i){ if(a[i]-'0'==f){ } else{ ans++; if(!vis[i]) vis[i]=1; //需要被交換 if(vis[i-1]) ans--; //前面一位也被交換,當前位不計貢獻 } f^=1; } memset(vis,0,sizeof(vis)); f=0; for(int i=1;i<=n;++i){ if(a[i]-'0'==f){ } else{ anss++; if(!vis[i]) vis[i]=1; if(vis[i-1]) anss--; } f^=1; } ans=min(ans,anss); printf("%d\n",ans);
C. Chef Monocarp
題意:一分鐘只能出一個餐,一開始所有餐都在烤爐裡,每次出餐貢獻+|t[i]−T|(T為當前時間),問讓總貢獻最小的方法。
(注:出餐時間不一定連續。
很顯然是一個dp題,但是我沒想到方程怎麼寫。
dp[i][j]表示第i號餐在j時間出來的最優解。
先排個序,因為時間是線性增長的,t[i]線性增長更優。
程式碼來自一位dalao
int dp[270][270*2],a[270],t,n; int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); memset(dp,0x7f,sizeof(dp)); for(int i=0;i<=2*n;++i) dp[0][i]=0; sort(a+1,a+1+n); for(int i=1;i<=n;++i){ for(int j=i-1;j<=2*n;++j){ for(int k=j+1;k<=2*n;++k){ dp[i][k]=min(dp[i][k],dp[i-1][j]+abs(a[i]-k)); } } } int ans=inf; for(int i=n;i<=2*n;++i) ans=min(ans,dp[n][i]); printf("%d\n",ans); } return 0; }
D. Minimal Height Tree
題意:給定樹的bfs序,對任意節點的子節點按升序排列,問最小高度是多少?
如果理解不了,我就來畫個圖吧。
兩個都是1 4 5 2 3的遍歷順序,明顯第一幅圖更優。
先看一下程式碼
int t,a[200007],q[200007],p,n; //q記錄每層的個數 int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); memset(q,0,sizeof(q)); q[0]=1;p=1; for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=2;i<=n;++i){ if(a[i]>a[i-1]){ //升序就加入當前層 q[p]++; } else if(q[p-1]>1){ //看一下上一層子結點個數大於1,掛到上層那個點 q[p-1]--; q[p]++; } else if(q[p-1]==1){ //上層個數==1,說明,上層已經沒有空間加了 p++; q[p]++; } } printf("%d\n",p); } return 0; }
還理解不了就對圖跑一遍,記住節點的子結點都必須升序。