8、开发商买土地
题目简介:
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
初见思路:
well,我完全没有什么思路一开始,我想的就是把所有的情况都算出来,就是横向分把所有的情况算出来,然后竖向分把所有的情况算出来,然后比出最小值,但是实现起来真的漏洞百出,我发现自己的数组操作真的和筛子一样,到处都是越界。所以,这也是我坚持把这道题debug出来的原因,我一定要看看自己到底什么地方没有注意到越界的问题。
但是最后,还是看了答案才写正确,笑死。
算法思路:
我觉得算法的思路确实也是把情况都算出来然后再进行比较,但是有一点优化就是他用了前缀和的想法简化了每次的运算我觉得,但是他不是额外存了一个前缀和,而是临时计算了这一行或者一列划分时的前缀和,但其实都一样,计算量是一样的,在之前先算出来,还是后面再算其实都是算一次而已。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
int n ,m ;
cin>>n>>m;
int sum =0;
vector<vector<int>> nums(n,vector<int>(m,0));
for(int i = 0 ;i < n ;i++){
for(int j = 0; j < m ; j++){
cin>>nums[i][j];
sum+=nums[i][j];
}
}
vector<int> col_div(n,0);
vector<int> row_div(m,0);
for(int i = 0;i<n;i++){
for(int j = 0;j<m;j++){
col_div[i] = col_div[i] + nums[i][j];
}
}
for(int j = 0;j<m;j++){
for(int i = 0;i<n;i++){
row_div[j] = row_div[j] + nums[i][j];
}
}
int col_temp = 0;
int min_res = INT32_MAX;
for(int i = 0 ; i < n ; i++){
col_temp+= col_div[i];
min_res = min(min_res, abs(sum - col_temp - col_temp));
}
int row_temp= 0;
for(int i = 0 ; i < m ; i++){
row_temp+= row_div[i];
min_res = min(min_res, abs(sum - row_temp - row_temp));
}
cout<<min_res<<endl;
}
9、移除链表元素
题目简介:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
初见思路:
这个题目知道,我自己的想法是,需要维护一个前节点pre
,然后一个当前节点cur
,这样子就可以在把当前节点cur
去掉后,能够让pre
和cur->next
联系起来;
感觉有点像,你把自己当作当前节点的话,你就要知道前节点是什么才行;然后后面的那个方法就感觉是,把自己当作是前节点,那我就得知道我的下一节点的下一节点;嗯,就是这样。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* vir_head = new ListNode();
ListNode* prve = vir_head;
ListNode* cur = head;
vir_head->next = head;
if( head != nullptr && head->val == val){
vir_head->next = head->next;
cur = vir_head->next;
prve = vir_head;
}
while(cur != nullptr){
if(cur->val == val){
cur = cur->next;
prve->next = cur;
}
else{
prve = cur;
cur =cur->next;
}
}
return vir_head->next;
}
};
这个虚拟节点的方法真是妙呀,我一开始还不知道怎么处理头节点呢,只想到了先把头节点存起来这样;
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while( head != nullptr && head->val == val){
head = head->next;
}
ListNode *cur = head;
while(cur!=nullptr){
if(cur->next != nullptr && cur->next->val == val){
cur->next = cur->next->next;
}
else{
cur = cur->next;
}
}
return head;
}
};
上面这个方法就是把自己当作前节点来思考了,比较关键的就是一定要先把头节点处理好,头节点不能是目标值所在的位置才行。
10、设计链表
题目简介:
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
初见思路:
救命,我想到了要自己顶一个node
,然后我看要根据index
来进行访问,所以我想着给每一个nod
e安排一个成员index
,然后遍历访问节点的index
来达到按index
访问元素的目的,结果…我完全不知道该怎么维护节点的index;还是算法中的while(n)
妙!
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int value) : val(value),next(nullptr) { }
};
MyLinkedList() : size(0){
dummyhead = new LinkedNode(0);
}
int get(int index) {
if (index > (size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = dummyhead->next; // 这样子,cur直接从第1个节点开始,n次后就到了第n个节点
while(index){
cur= cur->next;
index--;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* node = new LinkedNode(val);
node->next = dummyhead->next;
dummyhead->next = node;
size++;
}
void addAtTail(int val) {
LinkedNode* cur = dummyhead;
while(cur->next != nullptr){
cur = cur->next;
}
LinkedNode* node = new LinkedNode(val);
cur->next = node;
size++;
}
void addAtIndex(int index, int val) {
if(index > size) return;
if(index < 0) index = 0;
LinkedNode* cur = dummyhead;
while(index){
cur= cur->next;
index--;
}
LinkedNode* node = new LinkedNode(val);
node->next = cur->next;
cur->next = node;
size++;
}
void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
LinkedNode* cur = dummyhead;// 这样子,cur直接从第0个节点开始,n次后就到了第n-1个节点
while(index){ // cur->next 是第n个节点
cur= cur->next;
index--;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
size--;
}
void printLinkedList() {
LinkedNode* cur = dummyhead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
LinkedNode* dummyhead;
int size;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
确实感觉自己对于链表的理解完善了一下,我觉得最妙的就是虚拟头节点了!
LinkedNode* cur = dummyhead->next; // 这样子,cur直接从第1个节点开始,n次后就到了第n个节点
LinkedNode* cur = dummyhead;// 这样子,cur直接从第0个节点开始,n次后就到了第n-1个节点
这两个语句的运用真是出神入化!理顺了以后感觉一切都通畅了!
今天把昨天的开发商的题补了,但是又没时间写今天的第三题了,救命,明天一定补上!