2020 年百度之星·程序设计大赛 - 初赛二部分题解
Problem A
模拟即可
Problem B
由题可知每一个点都在以一个点为圆心,固定距离为半径的圆周上运动,那么要距离最小,直觉告诉我们是在同一条直线上,且位于同侧,但是看数据量发现,直接计算会炸时间,那么我们可以看看,一个距离在总和中有多少贡献,然后公式计算一下,可以优化到常数的时间复杂度。
Problem C
模拟就可以了,我们遍历每一个时间点,然后看在这个时间点的时候和现在感染的人在同一个位置的人,标记它,并且把这个点给pop出当前的轨迹。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005
#define fi first
#define se second
#define pb push_back
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
typedef vector <int> VI;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
void check_max (int &a,int b) { a=max (a,b);}
void check_min (int &a,int b) { a=min (a,b);}
int tt;
int main () {
scanf ("%d",&tt);
while (tt--) {
int n;
scanf ("%d",&n);
vector <queue <PII>> people (n);
int time=0;
for (int len,t,p,i=0;i<n;i++) {
scanf ("%d",&len);
rep (j,1,len) {
scanf ("%d%d",&t,&p);
people[i].push ({t,p});
time=max (time,t);
}
}
vector <bool> sign (n,0);
int pos[12]={0};
sign[0]=true;
rep (i,1,time) {
rev (j,0,n) {
if (sign[j]&&people[j].size ()&&people[j].front().first==i)
pos[people[j].front().second]=i;
}
rev (j,0,n) {
if (people[j].size ()&&people[j].front().first==i) {
if (pos[people[j].front().second]==i) sign[j]=true;
people[j].pop ();
}
}
}
printf ("1");
rep (i,2,n) if (sign[i-1]) printf (" %d",i);
printf ("\n");
}
return 0;
}
Problem D
爆搜是赛后提交会超时,标程是二分?
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005
#define fi first
#define se second
#define pb push_back
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
typedef vector <int> VI;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
void check_max (int &a,int b) { a=max (a,b);}
void check_min (int &a,int b) { a=min (a,b);}
string str;
int t,n;
map <int,int> mp;
void dfs (int cnt,int &ans,int sum[]) {
if (cnt==10) {
int k=inf;
rev (i,0,5) k=min (k,sum[i]);
ans=min (ans,n-k);
return ;
}
rev (i,0,5) {
sum[i]+=mp[cnt];
dfs (cnt+1,ans,sum);
sum[i]-=mp[cnt];
}
}
int main () {
ios::sync_with_stdio (false);
cin>>t;
while (t--) {
mp.clear ();
cin>>n;
rep (i,1,n) {
cin>>str;
mp[str.back ()-'0']++;
}
int ans=inf;
int sum[5]={0};
dfs (0,ans,sum);
cout<<ans<<endl;
}
return 0;
}
Problem E
最小费用最大流,我们可以发现一共就只有六种情况,所以我们把这六种情况压成点,然后圆点出发连三种饮料,cap是数量,然后发别连向每种情况,并加上对于的费用,然后每种情况连接到汇点,cap是情况的个数
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005
#define fi first
#define se second
#define pb push_back
typedef long long ll;
const int INF=0x3f3f3f3f;
typedef vector <int> VI;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
void check_max (int &a,int b) { a=max (a,b);}
void check_min (int &a,int b) { a=min (a,b);}
const int maxn=5e4+10;
const int M=5e3+10;
int n,m,s,t,cnt=1,tt;
int head[maxn],dis[maxn],pre[maxn],incf[maxn];
int maxflow,mincost;
bool vis[maxn];
struct edge {
int v,next,flow,cost;
}e[M<<1];
inline void add (int u,int v,int flow,int cost) {
e[++cnt]= (edge) {v,head[u],flow,cost};
head[u]=cnt;
}
inline void add_edge (int u,int v,int flow,int cost) {
add (u,v,flow,cost);
add (v,u,0,-cost);
}
inline bool spfa () {
queue <int > q;
MT (dis,0x3f);
MT (vis,0);
q.push (s);
dis[s]=0;
vis[s]=1;
incf[s]=1<<30;
while (q.size ()) {
int x=q.front (); q.pop ();
vis[x]=0;
for (int i=head[x];i;i=e[i].next) {
if (!e[i].flow) continue;
int v=e[i].v;
if (dis[v]>dis[x]+e[i].cost) {
dis[v]=dis[x]+e[i].cost;
incf[v]=min (incf[x],e[i].flow);
pre[v]=i;
if (!vis[v]) vis[v]=1,q.push (v);
}
}
}
if (dis[t]==inf) return 0;
return dis[t];
}
inline void mcmf () {
while (spfa ()) {
int x=t;
maxflow+=incf[t];
mincost+=dis[t]*incf[t];
int i;
while (x!=s) {
i=pre[x];
e[i].flow-=incf[t];
e[i^1].flow+=incf[t];
x=e[i^1].v;
}
}
}
int main () {
scanf ("%d",&tt);
while (tt--) {
MT (head,0);
cnt=1; maxflow=0,mincost=0;
int n,a,b,c;
scanf ("%d%d%d%d",&n,&a,&b,&c);
char str[4];
vector <int> v(6);
s=0,t=10;
rep (i,1,n) {
scanf ("%s",&str);
if (strcmp (str,"012")==0) v[0]++;
if (strcmp (str,"021")==0) v[1]++;
if (strcmp (str,"102")==0) v[2]++;
if (strcmp (str,"210")==0) v[3]++;
if (strcmp (str,"201")==0) v[4]++;
if (strcmp (str,"120")==0) v[5]++;
}
add_edge (s,1,a,0);
add_edge (s,2,b,0);
add_edge (s,3,c,0);
add_edge (1,4,INF,-3);
add_edge (1,5,INF,-3);
add_edge (1,6,INF,-2);
add_edge (1,7,INF,-1);
add_edge (1,8,INF,-2);
add_edge (1,9,INF,-1);
add_edge (2,4,INF,-2);
add_edge (2,5,INF,-1);
add_edge (2,6,INF,-3);
add_edge (2,7,INF,-2);
add_edge (2,8,INF,-1);
add_edge (2,9,INF,-3);
add_edge (3,4,INF,-1);
add_edge (3,5,INF,-2);
add_edge (3,6,INF,-1);
add_edge (3,7,INF,-3);
add_edge (3,8,INF,-3);
add_edge (3,9,INF,-2);
add_edge (4,10,v[0],0);
add_edge (5,10,v[1],0);
add_edge (6,10,v[2],0);
add_edge (7,10,v[3],0);
add_edge (8,10,v[4],0);
add_edge (9,10,v[5],0);
mcmf ();
printf ("%d\n",-mincost);
}
return 0;
}
Problem G
我们可以发现,我们的最优策略就是在我可以先完成这个任务的时候,那么我们先完成这个任务但是不提交,我们可以在对方完成的前一秒提交好贱啊,这样就浪费了对面的最多时间,然后这个时候我们却在做其他题目,这是最贪心的做法了,然后写一个dp就可以了。
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define MT(x,i) memset(x,i,sizeof(x) )
#define rev(i,start,end) for (int i=start;i<end;i++)
#define inf 0x3f3f3f3f
#define mp(x,y) make_pair(x,y)
#define lowbit(x) (x&-x)
#define MOD 1000000007
#define exp 1e-8
#define N 1000005
#define fi first
#define se second
#define pb push_back
typedef long long ll;
const ll INF=1e18+7;;
typedef vector <int> VI;
typedef pair<int ,int> PII;
typedef pair<int ,PII> PIII;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;};
void check_max (int &a,int b) { a=max (a,b);}
void check_min (int &a,int b) { a=min (a,b);}
const int maxn=2e3+1;
int t;
vector <vector <ll>> dp (maxn,vector <ll> (maxn,0));
ll a[maxn],b[maxn];
int main () {
scanf ("%d",&t);
while (t--) {
int n;
scanf ("%d",&n);
rep (i,1,n) scanf ("%lld",&a[i]);
rep (i,1,n) {
scanf ("%lld",&b[i]);
b[i]+=b[i-1];
}
dp.assign (n+1,vector <ll> (n+1,INF));;
dp[0][0]=0;
rep (i,1,n) {
rep (j,0,i) {
if (dp[i-1][j-1]+a[i]>b[i]||j==0) dp[i][j]=dp[i-1][j];
else dp[i][j]=min (dp[i-1][j],dp[i-1][j-1]+a[i]);
}
}
per (i,n,1) if (dp[n][i]!=INF) {
printf ("%d\n",i);
break;
}
}
return 0;
}