线段树- Lost Cows

本文介绍了一个有趣的排序问题——LostCows。在这个问题中,需要根据每头牛前面有多少头品牌较小的牛来确定它们的排队顺序。通过一种特殊的算法,可以逆向求解出原始的排序顺序。
Lost Cows
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 1876 Accepted: 1163

Description
N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands.

Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow.

Given this data, tell FJ the exact ordering of the cows.

Input
* Line 1: A single integer, N

* Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on.

Output
* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.

Sample Input

 

Sample Output

 

Source
USACO 2003 U S Open Orange

 解答方案

从后往前看,选择排名同输入相同的元素,然后从序列中删除此元素,进行重新选择。

比如:

n = 3

序列1 2 3中每个元素前面大于它的元素个数分别为0 1 2

输入为:0 1 1

则从最后一个1开始向前看,1 2 3 中前面大于该元素的元素个数为1的为数字1,因此选择数字1,然后将1从序列中删除(1 2 3->2 3,0 1 2->0 1)。然后看倒数第二个1。依此类推。

Code:

#include <stdio.h>
#include <string.h>

#define MAXN 8000

typedef struct
{
 int val;
 int index;
}treeNode;

static treeNode T[MAXN];
static int tlen;
static int input[MAXN];
static int output[MAXN];

static void buildT();
static void reorder(int );


int main(int argc,char **argv)
{
 int i,n;

 scanf("%d",&n);
 tlen = n;
 buildT();
 input[0] = 0;
 for(i=1;i<tlen;i++)
 {
  scanf("%d",&input[i]);
 }

    for(i=tlen-1;i>=0;i--)
 {
  reorder(i);
 }

 for(i=0;i<n;i++)
  printf("%d/n",output[i]);

 return 0;
}

static void buildT()
{
 for(int i=0;i<tlen;i++)
 {
  T[i].val = i;
  T[i].index = i+1;
 }
}

static void reorder(int index)
{
 int t = input[index];
 /*二分查找*/
 int low,hig,mid;
 bool found;

 low = 0;
 hig = tlen-1;
 found = false;

 while(low<=hig&&!found)
 {
  mid = (low+hig)>>1;
  if(T[mid].val==t)
   found = true;
  else if(T[mid].val<t)
   low = mid + 1;
  else
   hig = mid -1;
 }

 if(!found)
  return;

 output[index] = T[mid].index;
 
 for(int i=mid+1;i<tlen;i++)
 {
  memcpy(&T[i-1],&T[i],sizeof(treeNode));
  T[i-1].val--;
 }
 tlen -- ;
}

采用线段树的实现:

 

2
4
5
3
1

5
1
2
1
0
### 关于 BZOJ1728 Two-Headed Cows (双头牛) 的算法解析 此问题的核心在于如何通过有效的图论方法解决给定约束下的最大独立集问题。以下是详细的分析和解答。 #### 问题描述 题目要求在一个无向图中找到最大的一组节点集合,使得这些节点之间满足特定的颜色匹配条件。具体来说,每条边连接两个节点,并附带一种颜色标记(A 或 B)。对于任意一条边 \(u-v\) 和其对应的颜色 \(c\),如果这条边属于最终选取的子集中,则必须有至少一个端点未被选入该子集或者两端点均符合指定颜色关系。 #### 解决方案概述 本题可以通过 **二分枚举 + 图染色验证** 来实现高效求解。核心思想如下: 1. 假设当前最优解大小为 \(k\),即尝试寻找是否存在一个大小为 \(k\) 的合法子集。 2. 枚举每一个可能作为起点的节点并将其加入候选子集。 3. 对剩余部分执行基于 BFS/DFS 的图遍历操作,在过程中动态调整其他节点的状态以确保整体合法性。 4. 如果某次试探能够成功构建符合条件的大规模子集,则更新答案;反之则降低目标值重新测试直至收敛至最佳结果。 这种方法利用了贪心策略配合回溯机制来逐步逼近全局最优点[^1]。 #### 实现细节说明 ##### 数据结构设计 定义三个主要数组用于记录状态信息: - `color[]` : 存储每个顶点所分配到的具体色彩编号; - `used[]`: 表示某个定点是否已经被处理过; - `adjList[][]`: 记录邻接表形式表示的原始输入数据结构便于后续访问关联元素。 ##### 主要逻辑流程 ```python from collections import deque def check(k, n): def bfs(start_node): queue = deque([start_node]) used[start_node] = True while queue: u = queue.popleft() for v, c in adjList[u]: if not used[v]: # Assign opposite color based on edge constraint 'c' target_color = ('B' if c == 'A' else 'A') if color[u]==c else c if color[v]!=target_color and color[v]!='?': return False elif color[v]=='?': color[v]=target_color queue.append(v) used[v] =True elif ((color[u]==c)==(color[v]==('B'if c=='A'else'A'))): continue return True count=0 success=True for i in range(n): if not used[i]: temp_count=count+int(color[i]=='?' or color[i]=='A') if k<=temp_count: color_copy=color[:] if bfs(i): count=temp_count break else : success=False return success n,m=list(map(int,input().split())) colors=[['?']*m]*n for _ in range(m): a,b,c=input().strip().split() colors[int(a)-1].append((int(b),c)) low ,high,res=0,n,-1 while low<=high: mid=(low+high)//2 color=['?']*n used=[False]*n if check(mid,n): res=mid low=mid+1 else : high=mid-1 print(res) ``` 上述代码片段展示了完整的程序框架以及关键函数 `check()` 的内部运作方式。它接受参数 \(k\) 并返回布尔值指示是否有可行配置支持如此规模的选择[^2]。 #### 复杂度分析 由于采用了二分查找技术缩小搜索空间范围再加上单轮 DFS/BFS 时间复杂度 O(V+E),总体性能表现良好适合大规模实例运行需求。 ---
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值