9.5

T1
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 的顺序来运送他的奶牛

这道题不是很会。没有机房大佬的话做不出来。我不会严谨的证明贪心的正确性。易知,di越大那么奶牛就先被选取,就是说在排序的时候物品的di与它的排序权成正比,放在分母的上面。ti*2与排序中的权值成反比,所以放在分母。所以就按
a . d i / ( a . t i ∗ 2 ) &gt; b . d i / ( b . t i ∗ 2 ) a.di/(a.ti*2)&gt;b.di/(b.ti*2) a.di/(a.ti2)>b.di/(b.ti2) 决定先后顺序。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
struct node{
	int t,d;
}in[100010];
bool cmp(node a,node b){
	return a.d*(b.t*2)>b.d*(a.t*2);
}
int sum;
int ans;
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&in[i].t,&in[i].d);
		sum+=in[i].d;
	}
	sort(in+1,in+n+1,cmp);
	for(int i=1;i<=n;i++){
		sum-=in[i].d;
		ans+=sum*in[i].t*2;
	}
	printf("%lld\n",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。

这道题标准的单调队列优化动态规划。首先要推出基本的转移方程,再用单调队列去优化。
预处理前缀和数组s,f[i][1]表示取i的最大答案。f[i][0]表示不取i的最大答案。
f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 0 ] ) f[i][0]=max(f[i-1][1],f[i-1][0]) f[i][0]=max(f[i1][1],f[i1][0])
f [ i ] [ 1 ] = m a x ( f [ i ] [ 1 ] , f [ i ] [ 0 ] + s u m [ i ] − s u m [ j ] ) f[i][1]=max(f[i][1],f[i][0]+sum[i]-sum[j]) f[i][1]=max(f[i][1],f[i][0]+sum[i]sum[j]) i − k &lt; = j &lt; = i − 1 i-k&lt;=j&lt;=i-1 ik<=j<=i1
考虑优化,用单调队列动态维护
循环每一个i,所以要求f[i][0]+sum[j]的最大值。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int f[1000010][2];
int cnt;
int a[1000010];
int sum[1000010];
int q[1000010];
int mjc=0;
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	int l=1,r=1;
	for(int i=1;i<=n;i++){
		f[i][0]=max(f[i-1][0],f[i-1][1]);
		while(q[l]<i-m && l<=r){
			l++;
		}
		f[i][1]=sum[i]-sum[q[l]]+f[q[l]][0];
		while(f[i][0]-sum[i]>f[q[r]][0]-sum[q[r]]&&l<=r){
			r--;
		}
		q[++r]=i;
	}
	printf("%lld\n",max(f[n][1],f[n][0]));
	return 0; 
}

T3
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

判负环裸题。注意是入队大于n次。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int maxn=1050;
const int maxm=200050;

int n,m,k;
int t;
struct node{
	int to;
	int w;
	int next;
}e[maxm];
int cnt,head[maxn];
void add(int u,int v,int ww){
	e[++cnt].to=v;
	e[cnt].next=head[u];
	e[cnt].w=ww;
	head[u]=cnt;
} 
int flag=0;
int inq[maxn],in[maxn],dis[maxn];
void spfa(int s){
	queue<int> q;
	flag=0;
	memset(inq,0,sizeof(inq));
	memset(in,0,sizeof(in));
	memset(dis,inf,sizeof(dis));
	dis[s]=0;
	inq[s]=1;
	q.push(s);
	in[s]++;
	while(!q.empty()){
		int u=q.front();
		inq[u]=0;
		q.pop();
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!inq[v]){
					inq[v]=1;
					in[v]++;
					if(in[v]>n){
						flag=1;
						break;
					}
					q.push(v);
				}
			}
		}
	}
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		cnt=0;
		memset(head,0,sizeof(head));
		flag=0;
		scanf("%lld%lld%lld",&n,&m,&k);
		for(int i=1;i<=m;i++){
			int x,y,z;
			scanf("%lld%lld%lld",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);
		}
		for(int i=1;i<=k;i++){
			int x,y,z;
			scanf("%lld%lld%lld",&x,&y,&z);
			add(x,y,-z);			
		}
		for(int i=1;i<=n;i++){
			spfa(i);
			if(flag){
				printf("YES\n");
				break;
			}
		}
		if(!flag){
			printf("NO\n");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值