NOIP2014-5-24模拟赛
Problem 1 护花(flower.cpp/c/pas)
【题目描述】
约翰留下他的N(N<=100000)只奶牛上山采木.他离开的时候,她们像往常一样悠闲地在草场里吃草.可是,当他回来的时候,他看到了一幕惨剧:牛们正躲在他的花园里,啃食着他心爱的美丽花朵!为了使接下来花朵的损失最小,约翰赶紧采取行动,把牛们送回牛棚. 牛们从1到N编号.第i只牛所在的位置距离牛棚Ti(1≤Ti≤2000000)分钟的路程,而在约翰开始送她回牛棚之前,她每分钟会啃食Di(1≤Di≤100)朵鲜花.无论多么努力,约翰一次只能送一只牛回棚.而运送第第i只牛事实上需要2Ti分钟,因为来回都需要时间. 写一个程序来决定约翰运送奶牛的顺序,使最终被吞食的花朵数量最小.
【输入格式】
第1行输入N,之后N行每行输入两个整数Ti和Di
【输出格式】
一个整数,表示最小数量的花朵被吞食
【样例输入】
6
3 1
2 5
2 3
3 2
4 1
1 6
【样例输出】
86
【样例解释】
约翰用6,2,3,4,1,5的顺序来运送他的奶牛
Problem 2 修剪草坪(mowlawn.cpp/c/pas)
【题目描述】
在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。现在,
新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。
然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。FJ有N
(1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,
奶牛i的效率为E_i(0 <= E_i <=
1,000,000,000)。
靠近的奶牛们很熟悉,因此,如果FJ安排超过K(1<=K<=N)只连续的奶牛,那么,这些奶牛就会罢工
去开派对:)。因此,现在FJ需要你的帮助,计算FJ可以得到的最大效率,并且该方案中
没有连续的超过K只奶牛。
【输入格式】
* 第一行:空格隔开的两个整数N和K
* 第二到N+1行:第i+1行有一个整数E_i
【输出格式】
* 第一行:一个值,表示FJ可以得到的最大的效率值。
【样例输入】
5 2
1
2
3
4
5
输入解释:
FJ有5只奶牛,他们的效率为1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是
他不能选取超过2只连续的奶牛
【样例输出】
12
FJ可以选择出了第三只以外的其他奶牛,总的效率为1+2+4+5=12。
Problem 3 虫洞(wormhole.cpp/c/pas)
【题目描述】
John在他的农场中闲逛时发现了许多虫洞。虫洞可以看作一条十分奇特的有向边,并可以使你返回到过去的一个时刻(相对你进入虫洞之前)。John的每个农场有M条小路(无向边)连接着N (从1..N标号)块地,并有W个虫洞(有向边)。其中1<=N<=500,1<=M<=2500,1<=W<=200。 现在John想借助这些虫洞来回到过去(出发时刻之前),请你告诉他能办到吗。 John将向你提供F(1<=F<=5)个农场的地图。没有小路会耗费你超过10000秒的时间,当然也没有虫洞回帮你回到超过10000秒以前。
【输入格式】
* Line 1: 一个整数 F, 表示农场个数。
* Line 1 of each farm: 三个整数 N, M, W。
* Lines 2..M+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条用时T秒的小路。
* Lines M+2..M+W+1 of each farm: 三个数(S, E, T)。表示在标号为S的地与标号为E的地中间有一条可以使John到达T秒前的虫洞。
【输出格式】
* Lines 1..F: 如果John能在这个农场实现他的目标,输出"YES",否则输出"NO"。
【样例输入】
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
【样例输出】
NO
YES
T1:贪心
对于牛i和牛i+1,不妨假设之后的牛D的和为x,
那么如果牛i后运输,吃掉的花为: 2*Ti+1*(x+Di)+2*Ti*x
如果牛i先运输,吃掉的花为:2*Ti*(x+Di+1)+2*Ti+1*x
化简得:Ti+1*Di和Ti*Di+1
即
牛i后运输⇔Ti+1*Di<Ti*Di+1⇔Di/Ti<Di+1/Ti+1
所以排序之后使得任何一对牛都满足上述关系式,不可能存在更优解
注意:开longlong啊,遇到乘法想longlong,要不然爆0真是泪流满面啊~~~
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 100005 6 #define ll long long 7 using namespace std; 8 struct Node{ 9 ll T,D; 10 double p; 11 Node(){ 12 T=D=0; 13 p=0; 14 } 15 }s[MAXN]; 16 int n; 17 ll a[MAXN]; 18 bool comp(const Node &p1,const Node &p2){ 19 return (p1.p<p2.p); 20 } 21 int main() 22 { 23 // freopen("flower1.in","r",stdin); 24 // freopen("my.out","w",stdout); 25 scanf("%d",&n); 26 for(int i=1;i<=n;i++){ 27 scanf("%d%d",&s[i].T,&s[i].D); 28 s[i].p=(double)s[i].D/s[i].T; 29 } 30 sort(s+1,s+n+1,comp); 31 for(int i=1;i<=n;i++){ 32 a[i]=a[i-1]+s[i].D; 33 } 34 ll ans=0; 35 for(int i=n;i>=1;i--){ 36 ans+=(s[i].T*2*a[i-1]); 37 } 38 printf("%lld\n",ans); 39 return 0; 40 }
T2:
朴素dp很好想,滚动后70分
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 100005 6 #define ll long long 7 using namespace std; 8 ll f[MAXN]; 9 ll a[MAXN]; 10 int n,K; 11 int main() 12 { 13 // freopen("data.in","r",stdin); 14 scanf("%d%d",&n,&K); 15 for(int i=1;i<=n;i++){ 16 scanf("%lld",&a[i]); 17 } 18 for(int i=0;i<n;i++){ 19 ll temp=0; 20 for(int k=min(i,K);k>=0;k--){ 21 if(k+1<=K){ 22 f[k+1]=max(f[k+1],f[k]+a[i+1]); 23 } 24 temp=max(temp,f[k]); 25 } 26 f[0]=max(f[0],temp); 27 } 28 ll ans=0; 29 for(int i=0;i<=K;i++){ 30 ans=max(ans,f[i]); 31 } 32 printf("%lld\n",ans); 33 return 0; 34 }
正解是dp+单调队列
正难则反,求扔掉的牛的效率之和
然后f[i]表示
对于前i个牛,我们扔掉i,其余都是正常,
在这种情况下,所有扔掉的牛效率之和的最小值
那么f[i]=min{f[j]+a[i] | i-k-1<=j<=i-1}
实际上我们可以认为此方程中,i扔了,j扔了,i和j之间的都选
然后结果是∑a[i]-min{f[i-k]~f[i]}
这样状态的定义就具有了单调队列所需的“跳动性”,而不是普通dp的连贯性
一般dp连贯性固然好,但是优化时也应该学会打破常规,跳出去
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 100005 6 #define ll long long 7 using namespace std; 8 ll read(){ 9 ll x=0,f=1;char ch=getchar(); 10 while(ch<'0'||ch>'9'){if('-'==ch)f=-1;ch=getchar();} 11 while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 12 return x*f; 13 } 14 ll f[MAXN]; 15 int n,k; 16 ll a[MAXN]; 17 int deq[MAXN]; 18 ll sum; 19 int main() 20 { 21 // freopen("data.in","r",stdin); 22 n=read();k=read(); 23 for(int i=1;i<=n;i++){ 24 a[i]=read(); 25 sum+=a[i]; 26 } 27 int L=1,R=1; 28 for(int i=1;i<=n;i++){ 29 f[i]=f[deq[L]]+a[i]; 30 while(L<=R&&f[deq[R]]>=f[i]){ 31 R--; 32 } 33 deq[++R]=i; 34 if(i-k-1==deq[L]){ 35 L++; 36 } 37 } 38 ll ans=f[n]; 39 for(int i=n-1;i>=n-k;i--){ 40 ans=min(ans,f[i]); 41 } 42 printf("%lld",sum-ans); 43 return 0; 44 }
T3:
就是一个判断负环的问题,用dfs_SPFA即可
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #define MAXN 505 6 #define MAXM 3005 7 using namespace std; 8 int read(){ 9 int x=0,f=1;char ch=getchar(); 10 while(ch<'0'||ch>'9'){if('-'==ch)f=-1;ch=getchar();} 11 while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 12 return x*f; 13 } 14 int first[MAXN],Next[MAXM*2],to[MAXM*2],Val[MAXM*2],cnt; 15 //double edge 16 int d[MAXN],b[MAXN]; 17 int n,m,p; 18 void Add(int x,int y,int w){ 19 Next[++cnt]=first[x];first[x]=cnt;to[cnt]=y;Val[cnt]=w; 20 } 21 int SPFA(int x){ 22 //!!! 23 if(b[x]){ 24 return 1; 25 } 26 b[x]=1; 27 for(int e=first[x];e;e=Next[e]){ 28 int y=to[e],w=Val[e]; 29 if(d[y]>d[x]+w){ 30 d[y]=d[x]+w; 31 if(SPFA(y)){ 32 return 1; 33 } 34 } 35 } 36 b[x]=0; 37 return 0; 38 } 39 void solve(){ 40 memset(first,0,sizeof(first)); 41 memset(Next,0,sizeof(Next)); 42 memset(to,0,sizeof(to)); 43 memset(Val,0,sizeof(Val)); 44 memset(d,0,sizeof(d)); 45 memset(b,0,sizeof(b)); 46 cnt=0; 47 n=read();m=read();p=read(); 48 for(int i=1;i<=m;i++){ 49 int x,y,w; 50 x=read();y=read();w=read(); 51 Add(x,y,w); 52 Add(y,x,w); 53 } 54 for(int i=1;i<=p;i++){ 55 int x,y,w; 56 x=read();y=read();w=read(); 57 Add(x,y,-w); 58 } 59 for(int i=1;i<=n;i++){ 60 if(SPFA(i)){ 61 printf("YES\n"); 62 return ; 63 } 64 } 65 printf("NO\n"); 66 } 67 int main() 68 { 69 // freopen("wormhole.2.in","r",stdin); 70 int T; 71 T=read(); 72 for(int i=1;i<=T;i++){ 73 solve(); 74 } 75 return 0; 76 }