- 公牛和母牛
- 气象牛
- 轻轨
- 设计
T1: 公牛和母牛
题目描述
FJ想 N 头牛(公牛或母牛)排成一排接受胡总的检阅,经研究发现公牛特别好斗,如果两头公牛离得太近就会发生冲突,两头公牛之间至少要有 K (0<=K<=N)头母牛才能避免冲突。
FJ想请你帮忙计算一共有多少种放置方法,注意所有的公牛被认为是一样的,母牛也是,所以两种放置方法被认为不同当且仅当某些位置牛的种类不同。
输入
第一行:两个空格隔开的整数 N (N <=100000)和K。
输出
输出一个整数表示方法总数,答案可能很大,所以只需输出mod 5,000,011的值即可。
样例
输入
4 2
输出
6
样例解释
以下为6种放置方法,‘B’表示公牛,‘C’表示母牛
CCCC
BCCC
CBCC
CCBC
CCCB
BCCB
思路
这是一道极其简单的dp题(逃),其实就是每个位置的方案数 = 在这放公牛的方案数 + 在这放母牛的方案数
转移方程可以分为两部分
- 在 i <= k 时, dp[i] = i + 1 ,也就是只放一头公牛 + 全是母牛的方案和
- 在 i > k 时,dp[i] = dp[i - 1] + dp[i - k - 1] ,要是放母牛,那对前面没什么影响,所以放母牛的方案数为 dp[i - 1] ,放公牛的话,那他的前k个位置,就一定都是母牛,所以他可以自由排的就少了一截,所以就是 dp[i - k - 1]
然后,记得取模就ok啦
代码实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
int dp[maxn];
int main()
{
int n = 0, k = 0;
scanf("%d %d", &n, &k);
for(int i = 0; i <= n; i ++)
{
if(i <= k)
{
dp[i] = (i + 1) % 5000011;
//总位置i小于最小间隔的时候,最多只能放1只公牛,总方法数为i + 1
}
else
dp[i] = (dp[i - 1] + dp[i - k - 1]) % 5000011;
}
dp[n] = dp[n] % 5000011;
printf("%d",dp[n]);
return 0;
}
T2: 气象牛
题目描述
为了研究农场的气候,Betsy帮助农夫John做了 N(1 <= N <= 100)次气压测量并按顺序记录了结果 M_1 … M_N (1 <= M_i <= 1,000,000).
Betsy想找出一部分测量结果来总结整天的气压分布. 她想用 K (1 <= K <= N )个数 s_j
(1 <= s_1 < s_2 < … < s_K <= N )来概括所有测量结果. 她想限制如下的误差:
对于任何测量结果子集,每一个非此子集中的结果都会产生误差.总误差是所有测量结果的误差之和.更明确第说, 对于每一个和所有 s_j 都不同的 i :
- 如果 i 小于 s_1 , 误差是:2 * | M_i - M_(s_1) |
- 如果 i 在 s_j 和 s_(j+1) 之间,误差是:| 2 * M_i - Sum(s_j, s_(j+1)) |
注:Sum(x, y) = M_x + M_y; (M_x 和 M_y 之和) - 如果 i 大于 s_K ,误差为:2 * |M_i - M_(s_K) |
Besty给了最大允许的误差 E (1 <= E <= 1,000,000),找出最小的一部分结果使得误差最多为E.
详解
最开始我也没看懂题目,后来想一想,其实就是在给定的一群数中,选择若干个成为标准,然后,其他的数,要是在这个标准集合里,那就是对的。要是在标准集合里,那么就是有误差的。
首先,我们有一排数,假设粉色的两个是标准的话,
- 要是紫色的有误差,那么误差就等于,2 * (粉左1 与 紫错 的 差)
- 要是绿色的有误差,那么误差就等于,2 * 绿错 与 (两粉之 和) 的差**
- 要是胡萝卜色的有误差,那么误差就等于,2 * (粉右1 与 胡萝卜错 的 差)
输入
第一行: 两个空格分离的数: N 和 E
第2…N+1 行: 第 i+1 行包含一次测量记录:M_i
输出
第一行: 两个空格分开的数: 最少能达到误差小于等于 E 的测量数目和使用那个测量数目能达到的最小误差.
样例
输入
4 20
10
3
20
40
输出
2 17
样例解释
选择第二和第四次测量结果能达到最小误差17. 第一次结果的误差是2*|10-3| = 14;第三次结果的误差是|2*20 - (3+40)|=3.
思路
讨论的时候有说用 dp,然而本蒟蒻根本找不到转移方程,本题一下所有内容均摘自此处(膜拜大佬Orz)
大佬说:
解法:
首先我们预处理出一个数组 pre,pre[i][j] 保存在 i 到 j 之间元素对误差的贡献,即我们枚举 z (j-1 >= z >= i+1),计算abs(2*m[z]-m[i]-m[j])
特殊的是,我们还需要处理出 pre[i][0] 和 pre[i][n+1],分别表示在i之间和在 i 之后的元素对误差的贡献(
感觉贡献这个词怪怪的)
预处理时间复杂度 O(n3)
考虑如何DP
定义 DP[i][j] 表示前j个元素,必选第 j 个元素,总共选择了 i 个产生的最小误差。为什么把 i 放在前,j 放在后呢?因为我们首先最小化的是子集的大小。
状态转移方程就是:
dp[i][j] = min( dp[i][j] , dp[i-1][q] + sum )(i-1 <= q <= j)
我们有sum = -pre[q][n + 1] + pre[q][j] + pre[j][n + 1](之前我们是把 q 当成是子集的结尾并加上了它之后对误差的贡献,于是此时我们减去这个值改为用 j 来作为最后一个元素)
DP时间复杂度O(n3)
注意i = 1 的情况我们提前处理出来就是了
T3: 轻轨
题目描述
有N(1<=N<=20,000)个站点的轻轨站,有一个容量为C(1<=C<=100)的列车起点在1号站点,终点在N号站点,有K(K<=50,000)组牛群,每组数量为M_i(1<=M_i<=N),行程起点和终点分别为S_i和E_i(1<=S_i<E_i<=N)
计算最多有多少头牛可以搭乘轻轨。
输入
第1行:3个空格隔开的整数K,N,C
第2到K+1行:描述每组奶牛的情况,每行3个空格隔开的整数S_i,E_i和M_i
输出
输出一个整数表示最多可以搭乘轻轨的牛的数量。
样例
输入
8 15 3
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1
输出
10
样例解释
轻轨从1号站搭2头牛到5号站,搭3头牛从5好站到8号站,2头牛从8号站到14号站,1头牛从9号站到12号站,1头牛从13号站到14号站,1头牛从14号站到15号站。
思路
某不知名大佬说:这水题不就是从某谷上那道线段覆盖改的吗
显而易见的贪心
对于相同的起点的两批牛, 让他们中先下去的下去
对于任意一个时刻,如果车在此时已经满了,那么我们可以进行回退
即,将在他下车站以后才会下车而现在在车上的牛丢下去
显然这会更优,因为可以给后面的牛腾地方
可以维护一个will单调的队列,每次从后面开始抉择,但事实上只需要写一个优化的暴力
代码实现(胡说注释版)
#include<bits/stdc++.h>
using namespace std;
#define maxn 20000
#define maxc 50010
struct group
{
int num, from, to;
bool operator < (const group &z )const
{
return from^z.from ? from<z.from : to<z.to;
}
}cow[maxc];
int k, n, c, ans, will[maxc], now, Max;
int read()
{
char ch = getchar();
int ret = 0, flag = 1;
while(ch < '0'|| ch > '9')
{
if(ch == '-')
flag = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
ret = ret * 10 + ch - '0';
ch = getchar();
}
return ret * flag;
}
void solve(int train)
{
if((now + cow[train].num) <= c)//如果上车后还没满
{
now += cow[train].num;//当然是选择上车啦
will[cow[train].to] += cow[train].num;
//cow[train].to:在此站下牛的数量要加上这批牛的数量
Max = max(Max,cow[train].to);//现在车上这群牛最远要待到哪里
return;
}
else//不能上了
{
int what = now + cow[train].num - c;
//强上的话,多出来的牛的数量
for(int j = Max; (j > cow[train].to) && (what > 0); j --)
//从车上牛要到的最远站往回退,直到现在要上的这批牛的终点站
//也就是说,要是这是批坏牛,要坐的超过之前终点的话,就放弃他们
{
if(will[j])//在j站有牛下
{
int sth = will[j];
will[j] = max(will[j]-what,0);//看看是下的多还是上的多
Max = j;//最远站更新为j
sth -= will[j];
//为0则j站上的牛多,为正则为会多下的牛数
what -= sth;//看看还能多几个
}
}
will[cow[train].to] += cow[train].num - what;
//在这批牛终点站下车的牛数 = 这批 + 原来 - 被赶下去的
now = c;//重新放满
Max = max(Max,cow[train].to);//最后再更新一遍
}
}
int main()
{
k = read();
n = read();
c = read();
for(int i = 1; i <= k; i ++)
{
cow[i].from = read();
cow[i].to = read();
cow[i].num = read();
}
sort(cow + 1, cow + k + 1);//按照起始位置排序
int train = 1;//火车在的地方
for(int i = 1; i <= n; i ++)
{
if(will[i])//如果这一站有牛要下
{
ans += will[i];
//到站下了车,也算是在车上待过的牛了,要加进答案呢
now -= will[i];
//现在在车上的要减掉这站下车的
will[i] = 0;//这站的已经下完了
}
//如果该组的开头与火车现在的位置一样
while(cow[train].from == i)
{
solve(train);//详情请参考上文注释
train ++;//火车进入下一站
}
}
printf("%d",ans);
return 0;
}
T4: 设计
题目描述
和人一样,牛也喜欢站得离朋友较近的位置。FJ有 N (2 <= N <= 1,000)头牛,编号为1…N ,现在要设计一个顺序让他们站成一排给他们喂食。奶牛们按照编号顺序依次站立,允许有多只牛站在同一位置(也就是说,牛 i 和牛 j (i < j )的站立位置s_i ,s_j 一定满足 s_i <= s_j ,如果 s_i = s_j,那么编号为 i 到 j 之间的牛也一定站在 s_i 处)。
有一些牛相互喜欢,希望两人的距离在某个范围内,同样也有一些牛相互不喜欢,希望两人的距离大于等于某个距离,题目中给出 ML (1 <= ML <= 10,000)个限制描述相互喜欢的情况,给出 MD (1 <= MD <= 10,000 )个限制描述相互不喜欢的情况。
你的任务是计算,如果存在某种方案满足上述要求,输出 1 号牛和 N 号牛之间最大距离。
输入
第1行:3个空格隔开的整数N,ML,MD。
第2到ML+1行:每行包含3个空格隔开的整数A,B 和 D,满足1<= A < B <=N,表示牛 A 和牛 B 之间的距离不得超过 D (1<= D <=1,000,000)。
第ML + 2 到 ML + MD + 1 行:每行包含3个空格隔开的整数 A , B 和 D,满足1 <= A < B <= N ,表示牛A和牛B之间的距离至少为 D (1<= D <=1,000,000)。
输出
如果不存在这样的方案,输出 -1,如果牛1和牛N之间的距离 可以任意,输出 -2,否则输出 最大的距离 。
样例
输入
4 2 1
1 3 10
2 4 20
2 3 3
输出
27
样例解释
最佳方案是1到4号牛依次放置于位置0,7,10,27
思路
这显然是一个典型的差分约束系统,可以将喜欢和不喜欢都转化为不等式
{
d
i
s
[
y
]
−
d
i
s
[
x
]
≤
c
(
喜
欢
)
d
i
s
[
x
]
−
d
i
s
[
y
]
≤
c
(
讨
厌
)
\left\{ \begin{array}{rcl} dis[y]-dis[x] \leq c & & (喜欢)\\ dis[x]-dis[y] \leq c & & (讨厌)\\ \end{array} \right.
{dis[y]−dis[x]≤cdis[x]−dis[y]≤c(喜欢)(讨厌)
然后就可以开始跑最短路了,如果A和B距离至多为D则建边A->B权值为D,距离至少为D则建边B->A权值为 负D。
要是有负环,就输出 -1。那什么时候输出 -2 呢?
1 ~ n 没有关系,n可以无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限无限放大。
附
不能用 Dijkstra!!! 因为图中存在负边,所以只能用 SPFA或 Bellman_Ford算法。