Codeforces Round #486 (Div. 3)題解
這周趁著有Div3想上上分,過了3題,900多名。本以為這下上1400穩了吧,沒想到最後一看Rating,1399.。。。看來想變成青名還得繼續打嘍(希望別再掉下去)。
言歸正傳,我們來看一下這次Div3的題解
A題
這題確實比較水,由於輸出任意一組就可以,那麼就暴力了,使用used陣列標記一下之前出現的元素就可以嘍~
#include<bits/stdc++.h> using namespace std; int main() { int n,k; int cnt=0; int a[10000]; int used[10000]; int res[10000]; int u=0; int flag=0; scanf("%d%d",&n,&k); for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<n;i++) { if(!used[a[i]]) { cnt++; used[a[i]]=1; res[u++]=i+1; } if(cnt==k) { flag=1; break; } } if(flag==0) { printf("NO\n"); } else { printf("YES\n"); printf("%d",res[0]); for(int i=1;i<u;i++) printf(" %d",res[i]); printf("\n"); } return 0; }
B題
應該是考字串排序。若前一個能成為後一個的子串,那麼前一個串的長度必定不大於後一個,根據這個首先排個序。
接下來就是字串匹配了。可以暴力(因為串長度只有100),當然神犇也可以用KMP(覺得這道題寫KMP稍微有些浪費時間)。
#include<bits/stdc++.h> using namespace std; string a[110]; bool cmp(string x,string y) { return x.size()<y.size(); } int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) cin >> a[i]; sort(a,a+n,cmp); if(n==1) { printf("YES\n"); cout << a[0] <<endl; } else { int fff=1; for(int i=0;i<n-1;i++) { int ff=0; for(int j=0;j<=a[i+1].size()-a[i].size();j++) { int flag=1; int u=0; for(int k=j;k<j+a[i].size();k++) { if(a[i][u]!=a[i+1][k]) { flag=0; break; } else u++; } if(flag==1) { ff=1; break; } } if(ff==0) { fff=0; break; } } if(fff==1) { cout << "YES" << endl; for(int i=0;i<n;i++) cout << a[i] << endl; } else { printf("NO\n"); } } return 0; }
C題
這道題比賽的時候用了很長時間才做出來,歸根結底還是map用的不熟。
思路就是線上處理,每次讀入一列數就把這列數去掉一個元素後可能的情況存入map。我開了兩個map,一個存第幾組,一個存在每組數中的位置,也就是下標。(後來看了網上的程式碼可以用pair存,窩太弱了。。。)每次讀入一組新的數時,就看之前有沒有就行了。
#include<bits/stdc++.h> using namespace std; typedef long long ll; map<ll,int> mp1; map<ll,int> mp2; ll a[200010]; int main() { int n,r1,r2,r3,r4,t; int flag=0; scanf("%d",&t); for(ll i=1;i<=t;i++) { scanf("%d",&n); ll sum=0; for(ll j=1;j<=n;j++) { scanf("%lld",&a[j]); sum+=a[j]; } if(flag==1) continue; for(ll j=1;j<=n;j++) { map<ll,int>::iterator it=mp1.find(sum-a[j]); if(it!=mp1.end()) { if(mp1[sum-a[j]]!=i) { r1=mp1[sum-a[j]]; r2=mp2[sum-a[j]]; r3=i; r4=j; flag=1; break; } } else { mp1[sum-a[j]]=i; mp2[sum-a[j]]=j; } } } if(flag==0) printf("NO\n"); else { printf("YES\n"); printf("%d %d\n",r1,r2); printf("%d %d\n",r3,r4); } return 0; }
D題
可以說是一道數學題,也可以說是思維題
關鍵在於最多答案只有三個數且此時三個數成等差數列且公差為2的冪。(證明過程參考這位博主的部落格)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[200010];
set<ll> s;
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
s.insert(a[i]);
}
ll num=1;
int u=0;
ll res[3];
ll pre[100];
while(num<=2e9)
{
pre[u++]=num;
num*=2;
}
int tot=0;
int t1,t2;
for(int i=0;i<n;i++)
{
if(tot==2)
break;
for(int j=0;j<u;j++)
{
int tem=0;
t1=s.count(a[i]+pre[j]);
t2=s.count(a[i]+2*pre[j]);
if(t1&&t2)
tem=2;
else
{
if(t1||t2)
{
tem=1;
}
else
tem=0;
}
if(tem>tot)
{
if(tem==1)
{
res[0]=a[i];
if(t1)
res[1]=a[i]+pre[j];
if(t2)
res[1]=a[i]+2*pre[j];
}
else
{
res[0]=a[i];
res[1]=a[i]+pre[j];
res[2]=a[i]+2*pre[j];
}
tot=tem;
}
}
}
if(tot==0)
{
printf("1\n%lld\n",a[0]);
}
else if(tot==1)
{
printf("2\n");
printf("%lld %lld\n",res[0],res[1]);
}
else
{
printf("3\n");
printf("%lld %lld %lld\n",res[0],res[1],res[2]);
}
return 0;
}
E題
最開始想用BFS做,後來感覺過不了。後來看了別人的部落格,發現是貪心。
關鍵在於能被25整除,那麼末尾必是“00”,“25”,“50”。“75”。(這個應該很好理解)
那麼我們就檢查末兩位,如果不是就把前面的數換到後面就好了,最後再對前導0處理一下就ok。詳見程式碼
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
string a;
string b;
int len;
int tot;
void make(char x,char y)
{
int t1=0,t2=0,f=0;
if(a[len-1]==y)
{
t2=0;
f++;
}
else
{
for(int j=len-2;j>=0;j--)
{
if(a[j]==y)
{
for(int k=j;k<=len-2;k++)
{
swap(a[k],a[k+1]);
t2++;
}
f++;
break;
}
}
}
if(a[len-2]==x)
{
t1=0;
f++;
}
else
{
for(int j=len-3;j>=0;j--)
{
if(a[j]==x)
{
for(int k=j;k<=len-3;k++)
{
swap(a[k],a[k+1]);
t1++;
}
f++;
break;
}
}
}
int ind;
int ff=0;
if(f==2)
{
if(a[0]!='0')
tot=min(tot,t1+t2);
else
{
for(int i=1;i<=len-3;i++)
{
if(a[i]!='0')
{
ind=i;
ff=1;
break;
}
}
if(ff==1)
{
tot=min(tot,t1+t2+ind);
}
}
}
}
int main()
{
cin >> a;
b=a;
len=a.size();
tot=1000000000;
if(len==1)
printf("-1\n");
else
{
make('0','0');
a=b;
make('2','5');
a=b;
make('5','0');
a=b;
make('7','5');
if(tot==1000000000)
printf("-1\n");
else
printf("%d\n",tot);
}
return 0;
}
F題
又是dp題
參考了別人的部落格,這道題有兩種做法
第一種
dp[i][j]表示從0到i時,在點i有第j把傘(沒有傘時j==0)時的最小疲憊值
轉移時,分成三種情況(即對應著三種操作)
dp[i+1][j]=min(dp[i+1][j],dp[i][j]+w[j]) (第i+1個點下雨,同時沒帶傘時不成立,其他條件都成立) 該操作是從第i個點到第i+1個點啥也不做,即第i+1個點的狀態和第i個點的狀態相同
dp[i+1][0]=min(dp[i+1][0],dp[i][j]) (當下一個點沒下雨時成立) 該操作是在第i個點放下傘
dp[i+1][id[i]]=min(dp[i+1][id[i]],dp[i][j]+w[id[i]]) (在第i個點有傘) 該操作是第i個點有傘,拿起傘走到第i+1個點上去
#include<bits/stdc++.h>
#define INF 10000000000
using namespace std;
typedef long long ll;
typedef struct
{
int ind;
int w;
} U;
U ubr[2010];
int rain[2010];
int ok[2010];
ll dp[2010][2010];
bool cmp(U x,U y)
{
if(x.ind!=y.ind)
return x.ind<y.ind;
else
return x.w<y.w;
}
int main()
{
int a,n,m,t1,t2;
scanf("%d%d%d",&a,&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&t1,&t2);
for(int j=t1+1;j<=t2;j++)
{
rain[j]=1;
}
}
ubr[0].ind=-1;
ubr[0].w=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ubr[i].ind,&ubr[i].w);
}
sort(ubr,ubr+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(ok[ubr[i].ind]==0)
ok[ubr[i].ind]=i;
}
for(int i=0;i<=a;i++)
{
for(int j=0;j<=m;j++)
{
dp[i][j]=INF;
}
}
dp[0][0]=0;
for(int i=0;i<a;i++)
{
for(int j=0;j<=m;j++)
{
if(j||!rain[i+1])
dp[i+1][j]=min(dp[i+1][j],dp[i][j]+ubr[j].w);
if(!rain[i+1])
dp[i+1][0]=min(dp[i+1][0],dp[i][j]);
if(ok[i])
dp[i+1][ok[i]]=min(dp[i+1][ok[i]],dp[i][j]+ubr[ok[i]].w);
}
}
ll ans=INF;
for(int j=0;j<=m;j++)
ans=min(ans,dp[a][j]);
if(ans==INF)
printf("-1\n");
else
printf("%lld\n",ans);
return 0;
}
第二種
這種我只是看了一下就口胡了,沒有寫。看完之後感覺甚至比第一種好理解?(想看程式碼的話直接看上面的部落格就行)