2014-5-24 NOIP模拟赛
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的顺序来运送他的奶牛
/* 看到题目比较容易想到要贪心 对于每个对象有两个元素,一个是时间,一个是消费 由题意可知,我们希望先拿走时间短而消费多的 为了同时满足两个元素,我们把它合并成一个 按照消费/时间从大到小排序,然后计算出来,居然就A了 (老师说贪心考验人的勇气) */ #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n; long long ans,sum; struct node{ long long d,t; double dt; }a[100010]; int cmp(node x,node y){ if(x.dt!=y.dt)return x.dt>y.dt; if(x.d!=y.d)return x.d>y.d; return x.t<y.t; } int main(){ freopen("flower.in","r",stdin); freopen("flower.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&a[i].t,&a[i].d); a[i].dt=double(a[i].d)/double(a[i].t); sum+=a[i].d; } sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++){ sum-=a[i].d; ans+=a[i].t*sum*2; } printf("%I64d",ans); }
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。
#include<iostream> #include<cstdio> using namespace std; int n,m,a[100010]; long long ans; void dfs(int pos,long long sum,int w){ if(pos==n+1){ ans=max(ans,sum); return; } if(w==m){ dfs(pos+1,sum,0);//下一个牛不能选 } else{ dfs(pos+1,sum+a[pos],w+1);//下一个牛选 dfs(pos+1,sum,0);//下一个牛不选 } } int main(){ freopen("mowlawn.in","r",stdin); freopen("mowlawn.out","w",stdout); //freopen("Cola.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); dfs(1,0,0); printf("%d",ans); fclose(stdin); fclose(stdout); return 0; }
/* 考虑动归,在第i点时,在i-k到i中肯定有一个点j不能选择,即:j为断点。 所以f[i]=max(f[i],f[j-1]+a[j+1]+a[j+2]……a[i])(i-k<=j<=i) 所以维护前缀和,然后方程就变成了 f[i]=max(f[i],f[j-1]+sum[i]-sum[j]) (i-k<=j<=i) 变形一下变成:f[i]=max(f[i],f[j-1]-sum[j])+sum[i] (i-k<=j<=i) 发现max里面的值只与j有关,所以可以用单调队列优化转移。 */ #include<iostream> #include<cstdio> using namespace std; long long n,m,a[100010],sum[100010],f[100010]; long long d[100010]; int q[100010],head=0,tail=1; long long que(int i){//让返回值尽量的大,队列单调减,使首元素恒最大 d[i]=f[i-1]-sum[i]; while(head<=tail&&d[q[tail]]<d[i])tail--; q[++tail]=i; while(head<=tail&&q[head]<i-m)head++; return d[q[head]]; } int main(){ freopen("mowlawn.in","r",stdin); freopen("mowlawn.out","w",stdout); scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++)f[i]=que(i)+sum[i]; printf("%I64d",f[n]); }
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
/* spfa判负环 */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int t,n,m,w,head[25100*4],num,dis[5100],step[5100]; struct node{ int to,pre,v; }e[25100*4]; bool vis[5100]; void Insert(int from,int to,int v){ e[++num].to=to; e[num].v=v; e[num].pre=head[from]; head[from]=num; } int spfa(){ queue<int>q; q.push(1);step[1]++; dis[1]=0; vis[1]=1; while(!q.empty()){ int point=q.front();q.pop();vis[point]=0; for(int i=head[point];i;i=e[i].pre){ int to=e[i].to; if(dis[to]>dis[point]+e[i].v){ dis[to]=dis[point]+e[i].v; if(!vis[to]){ q.push(to); step[to]++; vis[to]=1; if(step[to]>n)return 1; } } } } return 0; } int main(){ freopen("wormhole.in","r",stdin); freopen("wormhole.out","w",stdout); scanf("%d",&t); while(t--){ memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); memset(step,0,sizeof(step)); memset(dis,127/3,sizeof(dis)); memset(vis,0,sizeof(vis)); scanf("%d%d%d",&n,&m,&w); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); Insert(x,y,z); Insert(y,x,z); } for(int i=1;i<=w;i++){ scanf("%d%d%d",&x,&y,&z); Insert(x,y,-z); } if(spfa())printf("YES\n"); else printf("NO\n"); } }
Problem 4 麻将(data.cpp/c/pas)
【题目描述】
众所周知,麻将是我们国家的国粹。这段时间,小D也迷上了麻将这个老少皆宜的游戏。
小D觉得这些不同规则的麻将太麻烦了,所以他集合了很多种麻将规则创造出了一套D麻将。下面是D麻将的几个特点:
D麻将中有三种花色,万(w)索(s)筒(t),每个花色下有9张牌,每张牌有4个。
D麻将中没有杠牌,只有顺子和刻子。顺子的含义是相同花色的三张连在一起的牌型(比如说2w3w4w);刻子的含义是三张花色和数字都相同的牌型(比如说2s2s2s)。
D麻将的胡牌的时候手上往往有14张牌,14张牌凑成了四个顺子或刻子和两张一样的牌做雀头就可以胡牌了。
D麻将胡牌的时候有很多种不同的牌型,不同的牌型会有不一样的番数。你的一种牌型可能满足了多个加番牌型,满足多个的情况下就把所有满足的牌型的番数全部加起来计算。
D麻将中有如下牌型可以加番:
平和(一番):4个顺子组成;
一杯口(一番):同花色同数值的顺子两组;
混全带幺九(一番):全部的顺子,刻子中都含有数字1或9;
三色同顺(一番):三种花色同数值的顺子各一组;
一气贯通(两番):由同花色一至九组成顺子;
对对和(两番):四组刻子;
断幺九(两番):胡牌的时候手上只有2-8的数字组成的牌型;
一色三顺(三番):同花色同数值顺子三组;
两杯口(三番):由两组不同的一杯口组成;
三色同刻(四番):三种花色同数值的刻子各一组;
清老头(五番):全部由1或9的刻子和雀头组成;
清一色(七番):全部由同一种花色的顺子,刻子及雀头组成;
比如说一个牌型为1s2s3s4s5s6s7s8s9s1s2s3s9s9s的牌,它满足了平和、一杯口、一气贯通、清一色四个牌型,所以它的番数是10番。
小D希望为D麻将做一个程序来帮忙判断这个牌型的番数是多少。
【输入格式】
输入第一行一个测试组数T。
接下来T行每行一个字符串s,表示需要判断番数的牌型。length(s)=28
【输出格式】
输出有T行每行一个整数,表示判断牌型的番数为多少。
【样例输入】
1
1s2s3s4s5s6s7s8s9s1s2s3s9s9s
【样例输出】
10