今天是NOIP考前模拟赛day3了.
发挥得海星,自我感觉良好.
T1:
题目大意:给定三种气球的数量a,b,c,两种或三种气球可以3个组成一组,求最多能分多少组.
考场得分:100.
显然我们设两种气球的数量为x,y,当且时,这两种气球的最多组数就是.
那么我们将读入进来的三个数排序之后存入a,b,c.当a为0时,我们发现答案就是;不为0时分两种情况,一种是,那么答案就为a+b;另一种是,那么答案就为.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
LL a[5],ans;
Abigail into(){
scanf("%lld%lld%lld\n",&a[1],&a[2],&a[3]);
}
Abigail work(){
sort(a+1,a+4);
if (a[1]==0){
if (a[2]==0) ans=0;
else if (a[2]*2>=a[3]) ans=(a[2]+a[3])/3;
else ans=a[2];
}else{
if (a[1]==a[2]&&a[2]==a[3]) ans=a[1];
else if ((a[1]+a[2])*2<=a[3]) ans=a[1]+a[2];
else ans=(a[1]+a[2]+a[3])/3;
}
}
Abigail outo(){
printf("%lld\n",ans);
}
int main(){
freopen("decorate.in","r",stdin);
freopen("decorate.out","w",stdout);
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}
实际上可以处理的更加简洁一些,我是由于考场上想出来之后不愿意花太多时间化简了,就直接写出来了.
T2:
题目大意:给定一大堆长度不超过10的字符串作为节点编号,给出从每个点开始可以直接走到哪些点,求是否有环.
考场得分:100.
看起来十分水的一道题,用一个拓扑排序判环即可解决这个问题.
但是这道题大概考的不是如何判环,考的是如何输入字符串——首先,字符串读入没有一行有多少个数的信息,只能靠getline;其次,还得考虑读入一行的字符串后如何分离;最后,还有一个将每一个字符串对应一个离散过后的点的过程.
所以我们用getline读入每一行,然后暴力分离每一个字符串,将其按照37进制数对应一个long long的数(不直接塞map里是怕超时),然后塞进map里对应一个数即可完成建图过程.
建完图之后就直接可以开始拓扑排序判环啦>_<.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
const int N=450000;
typedef long long LL;
map<LL,int> mmap;
int n,splay,top;
struct side{
int y,next;
}e[N+9];
int lin[N+9],deg[N+9];
string s;
struct Que{
int q[N+9],h,t;
Que(){h=1;t=0;}
bool empty(){return h>t;}
int front(){return q[h];}
void push(int x){q[++t]=x;}
void pop(){++h;}
}q;
LL strLL(int st,int en){
LL x=0;
for (int i=st;i<=en;i++)
if (s[i]<='9'&&s[i]>='0') x=x*37+s[i]-'0'+1;
else x=x*37+s[i]-'A'+11;
return x;
}
void ins(int x,int y){
e[++top].y=y;
e[top].next=lin[x];
lin[x]=top;
}
void topsort(){
Que();
for (int i=1;i<=splay;i++)
if (!deg[i]) q.push(i);
while (!q.empty()){
int t=q.front();q.pop();
for (int i=lin[t];i;i=e[i].next){
--deg[e[i].y];
if (deg[e[i].y]) continue;
q.push(e[i].y);
}
}
}
bool check(){
for (int i=1;i<=splay;i++)
if (deg[i]) return true;
return false;
}
Abigail into(){
int last;
mmap.clear();
scanf("%d",&n);getline(cin,s);
for (int i=1;i<=n;i++){
getline(cin,s);
int j=s.size()-1;
if (s[j]<='9'&&s[j]>='0'||s[j]<='Z'&&s[j]>='A') s=s+" ";
last=j=0;
LL x,y;
for (;j<s.size();j++)
if (s[j]<='9'&&s[j]>='0'||s[j]<='Z'&&s[j]>='A') break;
for (;j<s.size();j++)
if ((s[j]>'9'||s[j]<'0')&&(s[j]>'Z'||s[j]<'A')){
x=strLL(last,j-1);
if (mmap.find(x)==mmap.end()) mmap.insert(make_pair(x,++splay));
last=++j;
break;
}
for (;j<s.size();j++)
if ((s[j]>'9'||s[j]<'0')&&(s[j]>'Z'||s[j]<'A')){
y=strLL(last,j-1);
if (mmap.find(y)==mmap.end()) mmap.insert(make_pair(y,++splay));
ins(mmap[x],mmap[y]);
deg[mmap[y]]++;
last=j+1;
}
}
}
Abigail work(){
topsort();
}
Abigail outo(){
if (check()) puts("Yes");
else puts("No");
for (int i=1;i<=splay;i++)
deg[i]=0,lin[i]=0;
for (int i=1;i<=top;i++)
e[i].y=e[i].next=0;
splay=0;top=0;
}
int main(){
freopen("dependency.in","r",stdin);
freopen("dependency.out","w",stdout);
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}
T3:
题目大意:给定一张强连通图,让你将点1~b分为s组,使得每一组中每一对点的距离和最小,其中这里的距离是指两个点x,y,x到b+1的距离b+1到y的距离.
考场得分:0 / 65分(NOI Linux / 校内OJ).
爆0的原因是MLE了,校内OJ不知为何没有MLE...
考场上我的思路就是发现我们肯定要先跑最短路,但是由于时间不够了我写了个floyd.
那么我们很容易想到一个贪心就是距离b+1越小的点越优秀,所以我们肯定让距离b+1越小的点分配在一个任务里的数量越多,那么我们就可以搞出一个十分直接的贪心.
但是贪心的错误率比较高,所以我们先按照到b+1的距离从小到大排序,然后用一个DP,用f[i][j]表示i个点分配j个任务的最小距离和,那么就可以列出方程:
其中sum[i][j]表示排序后在区间[i,j]里的点分配在一个任务里的贡献,可以预处理出.
我们发现这个算法十分优秀,时间复杂度为,能够拿到65分的高分,但是我因为开了三个的long long数组而MLE了.
考场MLE代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=5000;
const LL INF=(1LL<<60LL)-1LL;
int n,m,b,s;
LL dis[N+9][N+9],sum[N+9][N+9],f[N+9][N+9];
struct node{
int x,v;
bool operator < (const node &p)const{return v<p.v;}
}ord[N+9];
void floyd(){
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
Abigail into(){
scanf("%d%d%d%d",&n,&b,&s,&m);
int x,y;LL l;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dis[i][j]=INF;
for (int i=1;i<=m;i++){
scanf("%d%d%lld",&x,&y,&l);
dis[x][y]=min(dis[x][y],l);
}
}
Abigail work(){
floyd();
for (int i=1;i<=b;i++)
ord[i].x=i,ord[i].v=dis[i][b+1]+dis[b+1][i];
sort(ord+1,ord+1+b);
for (int len=1;len<=n;len++)
for (int i=1;i+len-1<=n;i++){
sum[i][i+len-1]=sum[i][i+len-2];
for (int j=i;j<=i+len-2;j++)
sum[i][i+len-1]+=dis[ord[j].x][b+1]+dis[b+1][ord[i+len-1].x]+dis[ord[i+len-1].x][b+1]+dis[b+1][ord[j].x];
}
for (int i=0;i<=n;i++)
for (int j=0;j<=s;j++)
f[i][j]=INF;
f[0][0]=0;
for (int i=1;i<=b;i++)
for (int j=1;j<=s;j++)
for (int k=0;k<i;k++)
f[i][j]=min(f[i][j],f[k][j-1]+sum[k+1][i]);
}
Abigail outo(){
printf("%lld\n",f[b][s]);
}
int main(){
freopen("assignment.in","r",stdin);
freopen("assignment.out","w",stdout);
into();
work();
outo();
return 0;
}
正解是在上面这个朴素DP的基础上,进行一个神奇的优化.
显然,这个DP的转移过程中,一个区间的大小肯定大于后面的区间的大小,所以上述转移中的k一定满足.
所以时间复杂度为,由于调和级数约等于log,所以时间复杂度为,由于实现较为宽松且调和级数跑不满,所以可以AC.
然后将floyd换成两边dijkstra,sum数组换成使用一组内距离之和*(元素个数-1)即可.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=5000,M=50000,inf=(1<<30)-1;
const LL INF=(1LL<<60LL)-1LL;
int n,m,b,s;
LL f[2][N+9],sum[N+9],now=0,old=1;
struct side{
int y,next,v;
}e[M*2+9];
int lin[N+9][N+9],dis[2][N+9],top,use[N+9];
struct state{
int x,v;
state(){x=v=0;}
state(int xx,int vv){x=xx;v=vv;}
bool operator > (const state &p)const{return v>p.v;}
bool operator < (const state &p)const{return v<p.v;}
}ord[N+9];
priority_queue<state,vector<state>,greater<state> >qmin;
void ins(int t,int x,int y,int v){
e[++top].y=y;e[top].v=v;
e[top].next=lin[t][x];
lin[t][x]=top;
}
void dijkstra(int t,int st){
for (int i=1;i<=n;i++) dis[t][i]=inf,use[i]=0;
dis[t][st]=0;qmin.push(state(st,0));
while (!qmin.empty()){
int tt=qmin.top().x;qmin.pop();
if (use[tt]) continue;
use[tt]=1;
for (int i=lin[t][tt];i;i=e[i].next)
if (dis[t][tt]+e[i].v<dis[t][e[i].y]){
dis[t][e[i].y]=dis[t][tt]+e[i].v;
qmin.push(state(e[i].y,dis[t][e[i].y]));
}
}
}
Abigail into(){
scanf("%d%d%d%d",&n,&b,&s,&m);
int x,y;LL l;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&l);
ins(0,x,y,l);ins(1,y,x,l);
}
}
Abigail work(){
dijkstra(0,b+1);
dijkstra(1,b+1);
for (int i=1;i<=b;i++)
ord[i].x=i,ord[i].v=dis[1][i]+dis[0][i];
sort(ord+1,ord+1+b);
for (int i=1;i<=n;i++)
sum[i]=sum[i-1]+LL(dis[0][ord[i].x])+LL(dis[1][ord[i].x]);
for (int i=1;i<=b;i++)
f[now][i]=sum[i]*LL(i-1);
for (int t=2;t<=s;t++){
now^=1;old^=1;
for (int i=0;i<=b;i++)
f[now][i]=INF;
for (int i=1;i<=b;i++)
for (int j=i-i/t;j<i;j++)
f[now][i]=min(f[now][i],f[old][j]+(sum[i]-sum[j])*LL(i-j-1));
}
}
Abigail outo(){
printf("%lld\n",f[now][b]);
}
int main(){
freopen("assignment.in","r",stdin);
freopen("assignment.out","w",stdout);
into();
work();
outo();
return 0;
}