状态压缩dp
朋友的笔试题,不是面试题
思路:
求出选择能够覆盖的最小卡片的数量,容易想到是动态规划,主要是状态表示和转移怎么做是一个难点,一看数据范围比较小,多半就是状态压缩dp,
假如数组下标可以存储字符串,咱们可以这样来记录状态
用dp[i][“12359”]来表示从0到i中选择数字集合12359的最小步数,状态转移也就容易推出了。
但是因为不能用字符串表示,因此我们可以用二进制位来表示每一个数字是否出现
状态表示:
dp[i][state] 从索引0到索引i中,达到state展示的数字所需要的最小卡片数量(state表示的是一系列数字)
state 第一位代表是不是0
state 第二位代表是不是1
state 第三位代表是不是2
num=0 state=0000000001
num=1 state=0000000010
num=2 state=0000000100
num=4 state=0000010000
num=9876543210 state=1111111111
状态转移:
dp[i][cur_state]= dp[i-1][cur_state],dp[i-1][pre_state]+1
注意:
状态转移是需要判断前一个状态是不是可以达到当前状态,假设用到当前索引i的选择数字的二进制表示的状态是num
必须cur_state==pre_state|num
伪代码:
初始化dp[][]=maxvalue;
dp[0][0]=0;
dp[0][num]=1;
for(int i=1;i<len;i++){
int num;
for(int cur=0;j<1<<10;j++){
dp[i][cur]=dp[i-1][cur];
for(int pre=0;k<1<<10;k++){
if ( (pre|num) ==cur )dp[i][cur]=Math.min(dp[i][pre]+1,dp[i][cur]);
}
}
}
*/
import java.util.Arrays;
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
String[] ss = line.split(" ");
final int len = 1 << 10;
int dp[][] = new int[ss.length][len];
for (int i = 0; i < dp.length; i++) Arrays.fill(dp[i], dp.length + 10);
for (int i = 0; i < ss.length; i++) {
int n, m, k;
n = ss[i].charAt(0) - '0';
m = ss[i].charAt(1) - '0';
k = ss[i].charAt(2) - '0';
//数字怎样转换成想要的状态num呢?
int num = 1 << n | 1 << m | 1 << k;
if (i == 0) {
dp[0][0] = 0;
dp[0][num] = 1;
continue;
}
for (int cur = 0; cur < len; cur++) {
dp[i][cur] = dp[i - 1][cur];
for (int pre = 0; pre < len; pre++) {
if ((pre | num) == cur) dp[i][cur] = Math.min(dp[i][pre] + 1, dp[i][cur]);
}
}
}
if (dp[ss.length - 1][(1 << 10) - 1] <= ss.length) System.out.println(dp[ss.length - 1][(1 << 10) - 1]);
else System.out.println(-1);
}
}
dijkstra堆优化
不是面试题,这是朋友的笔试题。
题目介绍:输入一个二维数组,要求从二维数组左上角走到右下角的最少代价,只能向下或者左右走,每走一步路的代价为1或者2,当起点的数组值等于终点的数组值就是1否则是2.
输入
3 3
1 0 0
1 1 1
0 1 1
输出
4
数据范围:n,m<=500
样例解释(0,0)-(1,0)-(1,1)-(1,2)-(2,2)
走的都是1没有走0所以每一步的代价都是1,值就是4
题解:由于nm都是小于500,所以二维矩阵最多有250000个点,其实每个点最多只有三条边,求稀疏图可以用堆优化的dijkstra,由于不是自己手写的堆,达不到mlong(n),用的是优先级队列,我的解法就是mlog(m)。ps:java写图论的题直接爆炸,最好用c++,初始化太麻烦了,还没有pair,写起来是真的不方便
package com.company;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
static int n;
static int m;
static final int MAX = 250010;
static int[][] t;
static int d[];
static boolean tg[];
static ArrayList<int[]>[] g;
static PriorityQueue<int[]> q;
static int dx[] = {0, 0, 1};
static int dy[] = {1, -1, 0};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
init();
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
t[i][j] = sc.nextInt();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int v1 = i * m + j + 1;
for (int k = 0; k < 3; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x < 0 || x >= n || y < 0 || y >= m) continue;
int v2 = x * m + y + 1;
int c = 0;
if (t[i][j] == t[x][y]) c = 1;
else c = 2;
g[v1].add(new int[]{v2, c});
}
}
}
dijkstra();
}
private static void init() {
t = new int[n][m];
q = new PriorityQueue<>((a, b) -> {
return a[1] - b[1];
});
tg = new boolean[MAX];
g = new ArrayList[MAX];
d = new int[MAX];
Arrays.fill(d, MAX);
for (int i = 1; i <= n * m; i++) g[i] = new ArrayList<>();
}
private static void dijkstra() {
d[1] = 0;
q.add(new int[]{1, 0});
while (!q.isEmpty()) {
int[] poll = q.poll();
if (tg[poll[0]]) continue;
tg[poll[0]] = true;
//v w
for (int[] t : g[poll[0]]) {
//poll[0] t[0] t[1]
if (d[t[0]] > d[poll[0]] + t[1]) {
d[t[0]] = d[poll[0]] + t[1];
q.add(new int[]{t[0], d[t[0]]});
}
}
if (tg[n * m]) break;
}
System.out.println(d[n * m]);
}
}
分解质因数
思路:从小到大查找每一个质因数,因为是从小到大进行遍历,所以容易证明不存在遗漏(数学归纳法),又因为一个数字num的质因子,最多只有一个比sqrt(num)大,所以只需要进行遍历sqrt(num)次数就可以,最后假如剩下的数字大于一那就是质因子。
void fun(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0){
int cnt=0;
while(x%i==0){
x/=i;
cnt++;
}
cout<<i<<' '<<cnt<<endl;
}
}
if(x>1)cout<<x<<' '<<1<<endl;
}
最长回文子序列
思路:动态规划,从长度较短的字符串开始计算,依次迭代计算长度更长的字符串
状态表示:dp[x][y]
字符串第x索引到第y哥索引之间的最长回文子序列的长度
状态转移方程:
根据x所在索引和y所在索引的字符是否相等
- 相等
dp[x][y]=dp[x+1][y-1]+2
- 不相等
dp[x][y]=max(dp[x+1][y],dp[x][y-1])
状态初始化:
单个字符的最长回文子串序列为1
小问题:
-
如果长度是2,并且x和y所在索引的字符相等
dp[x][y]=2
-
由状态转移方程,后面的状态依赖前面的状态,所以要掌握按照长度来遍历字符串
//举例遍历字符串s //len 表示遍历字符串的长度,start表示字符串的起始索引,字符串表示为s[start][start+len-1]; for(int len=1;len<s.size();len++){ for(int start=0;start<s.size();s++){ if(start+len-1>s.size())break; //... } }
int dp[1010][1010];
int longestPalindromeSubseq(string s) {
int len=s.size();
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
if(i+j>len)break;
if(i==0)dp[j][j]=1;
else if(s[j]==s[i+j]){
if(i==1)dp[j][i+j]=2;
else dp[j][i+j]=dp[j+1][i+j-1]+2;
}else dp[j][i+j]=max(dp[j+1][i+j],dp[j][i+j-1]);
}
}
return dp[0][len-1];
}
位运算
位运算必须掌握的俩个基本操作
- 查找一个数字的二进制表示中第i位是多少
- 返回一个数字二进制表示中最后一个1 (lowbit)
leetcode
leetcode服务器程序编译的时候使用了 AddressSanitizer 工具,会对有符号数的左移位操作做保护,强制转成无符号数做移位可绕过,一些其他的题库集不会出现这这个问题
题目
思路:两个数的和可以转换为不进位和进位的和
非递归解法
class Solution {
public:
int getSum(int a, int b) {
int t=0;
while(b!=0){
t=(unsigned int)(a&b)<<1;
a=a^b;
b=t;
}
return a;
}
};
递归解法
class Solution {
public:
int getSum(int a, int b) {
if((a&b)==0)return a^b;
return getSum(a^b,(unsigned int)(a&b)<<1);
}
};
思路:相减之后查看符号位,可以知道大小
class Solution {
public:
int maximum(int a, int b){
long sub=(long long) b-(long long)a;
int k=sub>>63&1;
return k*a+(k^1)*b;
}
};
class Solution {
public:
int cnt(int num){
int res=0;
while(num){
num-=num&-num;
res++;
}
return res;
}
vector<int> countBits(int n) {
vector<int>res;
for(int i=0;i<=n;i++){
res.push_back(cnt(i));
}
return res;
}
};
-
思路:将所有数字元素异或得到结果为两个出现一次数字的异或,再根据lowbit运算得到异或的第一个一(异或为一说明原本两个数这一位不同)按照这一位进行分为两类,在进行异或可以得到两个结果
细节:int_min =1000000,也就是-0,移位会出现报错,需要额外进行判断
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
vector<int>res;
int xorsum=0;
for(int i=0;i<nums.size();i++)xorsum^=nums[i];
int num=xorsum == INT_MIN ? xorsum : xorsum & (-xorsum);
int x1=0,x2=0;
for(int i=0;i<nums.size();i++){
if(nums[i]&num)x1^=nums[i];
else x2^=nums[i];
}
res.push_back(x1);
res.push_back(x2);
return res;
}
};
依次计算第0位到第31位的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res=0;
for(int i=0;i<32;i++){
int cnt=0;
for(int j=0;j<nums.size();j++)
cnt+=nums[j]>>i&1;
cnt%=3;
if(cnt)res|=1<<i;
}
return res;
}
};
8,9.10.12 int_min 1000000,13
蓄水池抽样算法
class Solution {
public:
ListNode* hd;
Solution(ListNode* head) {
hd=head;
}
int getRandom() {
int res;
int i=1;
for(ListNode* t=hd;t;t=t->next,i++){
if(rand()%i==0)res=t->val;
}
return res;
}
};
c(N-1,K-1) / c(n,K) = K/N
字符串匹配
有两个字符串,子串/母串子串=abc 母串=dabef(cba)kkk
让在母串中,找到子串第一次匹配位置
注解:子串的位置可以变化 要求时间复杂度o(n)
思路:提前用一个数组将字符串a中的每个字母的出现次数进行记录,遍历时只需要比较原本的字符串跟现在的字符串的出现的字符以及次数就行,代码实现时,使用了cnt变量来进行一个巧妙的处理。
#include<iostream>
using namespace std;
int m[26];
int find(string a,string b){
if(a.size()>b.size())return -1;
for(int i=0;i<a.size();i++)m[a[i]-'a']--;
int cnt=0;
for(int i=0;i<b.size();i++){
if(m[b[i]-'a']++<0)cnt++;
if(i>=a.size())
if(--m[b[i-a.size()]-'a']<0)cnt--;
if(cnt==a.size())return i-a.size()+1;
}
return -1;
}
int main(){
string a,b;
cin>>a>>b;
int res=find(a,b);
cout<<res<<endl;
return 0;
}
腐烂的橘子
class Solution {
public:
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int xin=0;
int orangesRotting(vector<vector<int>>& grid) {
int res=0;
queue<pair<int,int>>q;
pair<int,int>cur;
pair<int,int>last;
for(int i=0;i<grid.size();i++){
for(int j=0;j<grid[i].size();j++){
if(grid[i][j]==2){
cur={i,j};
q.push({i,j});
}
else if(grid[i][j]==1)xin++;
}
}
if(!xin)return 0;
last=cur;
while(q.size()){
pair<int,int>p=q.front();
q.pop();
for(int i=0;i<4;i++){
int x=dx[i]+p.first;
int y=dy[i]+p.second;
if(x>=0&&x<grid.size()&&y>=0&&y<grid[0].size()&&grid[x][y]==1){
grid[x][y]=2;
cur={x,y};
q.push({x,y});
xin--;
}
}
if(p==last){
res++;
last=cur;
}
}
if(xin)return -1;
return res-1;
}
};
二叉树的最近公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root)return NULL;
if(root==p||root==q)return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(left&&right)return root;
if(left)return left;
else return right;
}
};
二叉搜索树的题型
- 判断二叉搜索树
- BiNode
- 二叉搜索树的最小差值
- 第k小个元素
- 后继者(不推荐)
二叉树的非递归遍历
- 二叉搜索迭代器
有返回值的中序遍历
-
二叉树的直径
-
二叉树的坡度
-
二叉树的最大路径和
-
二叉树的最近公共祖先
其他
- 后继者
判断二叉搜索树
class Solution {
public:
long pre=LONG_MIN;
bool res=true;
void inorder(TreeNode* root){
if(root==nullptr||res==false)return;
if(root->left)inorder(root->left);
if(root->val <= pre)res=false;
pre=root->val;
if(root->right)inorder(root->right);
}
bool isValidBST(TreeNode* root) {
inorder(root);
return res;
}
};
BiNode
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* head=NULL;
TreeNode* pre=NULL;
void inorder(TreeNode* root){
if(root==NULL)return ;
if(root->left)inorder(root->left);
if(head==NULL){
head=root;
}
else{
pre->left=NULL;
pre->right=root;
}
pre=root;
if(root->right)inorder(root->right);
}
TreeNode* convertBiNode(TreeNode* root) {
inorder(root);
if(tail!=NULL){
tail->left=NULL;
tail->right=NULL;
}
return head;
}
};
二叉搜索树的最小差值
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res=1e5;
TreeNode* pre=nullptr;
void inorder(TreeNode* root){
if(root){
if(root->left)inorder(root->left);
if(pre==nullptr)pre=root;
else{
res=min(res,abs(root->val-pre->val));
pre=root;
}
if(root->right)inorder(root->right);
}
}
int getMinimumDifference(TreeNode* root) {
inorder(root);
return res;
}
};
二叉搜索树中第k小个元素
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int t;
int res;
void inorder(TreeNode* root){
if(root){
if(root->left)inorder(root->left);
if(t==1)res=root->val;
t--;
if(root->right)inorder(root->right);
}
}
int kthSmallest(TreeNode* root, int k) {
t=k;
inorder(root);
return res;
}
};
二叉搜索迭代器
考察二叉树的中序非递归遍历,自己没写
class BSTIterator {
public:
stack<TreeNode*> S;
BSTIterator(TreeNode* root) {
if(!root)
return;
// 初始时, 将左子树一直入栈
TreeNode* p = root;
while(p) {
S.push(p);
p = p->left;
}
}
/** @return the next smallest number */
int next() {
if(!hasNext())
return -1;
TreeNode* ans = S.top(); S.pop(); // 栈顶元素, 就是要访问的next
// 中序遍历, 栈顶元素出栈后, 要将右子树和右子树的所有左子树都入栈
TreeNode* p = ans->right;
while(p){
S.push(p);
p = p->left;
}
return ans->val;
}
/** @return whether we have a next smallest number */
bool hasNext() {
return !S.empty();
}
};
另一种方式
class BSTIterator {
private:
vector<TreeNode*> S;
public:
BSTIterator(TreeNode* root){
while(root){
S.push_back(root);
root = root->left;
}
}
int next() {
TreeNode* t = S.back();
S.pop_back();
int val = t->val;
t = t->right;
while(t){
S.push_back(t);
t = t->left;
}
return val;
}
bool hasNext() {
return !S.empty();
}
};
后继者
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(root==NULL||p==NULL)return NULL;
if(root->val<=p->val)return inorderSuccessor(root->right,p);
TreeNode* left=inorderSuccessor(root->left,p);
return left ? left : root;
}
};
二叉树的直径
需要掌握返回树的长度的写法
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res=0;
int inorder(TreeNode* root){
if(!root)return 0;
int left=0,right=0;
if(root->left)left=inorder(root->left);
if(root->right)right=inorder(root->right);
res=max(res,left+right+1);
return 1+max(left,right);
}
int diameterOfBinaryTree(TreeNode* root) {
inorder(root);
return res-1;
}
};
二叉树的坡度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res=0;
int inorder(TreeNode *root){
if(!root)return 0;
int left=0,right=0;
if(root->left)left=inorder(root->left);
if(root->right)right=inorder(root->right);
res+=abs(left-right);
return root->val+left+right;
}
int findTilt(TreeNode* root) {
inorder(root);
return res;
}
};
二叉树的最近公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* res=NULL;
int inorder(TreeNode* root, TreeNode* p, TreeNode* q){
if(!root||res)return 0;
int left=0,right=0;
if(root->left)left=inorder(root->left,p,q);
if(root->right)right=inorder(root->right,p,q);
int t=0;
if(root==p||root==q)t=1;
t+=left+right;
if(t==2&&!res)res=root;
return t;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
inorder(root,p,q);
return res;
}
};
二叉树的最大路径和
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int res=-1010;
//返回包含根的最大分支
int inorder(TreeNode* root){
if(!root)return 0;
int left=-1001,right=-1001;
if(root->left)left=inorder(root->left);
if(root->right)right=inorder(root->right);
left=max(left,0);
right=max(right,0);
res=max(res,root->val+left+right);
return root->val+max(left,right);
}
int maxPathSum(TreeNode* root) {
if(!root)return 0;
inorder(root);
return res;
}
};
链表排序
class Solution {
public:
// 找中间节点(顺便将链表一分为二) [head, mid], [mid->next, 末尾节点]
ListNode* getMid(ListNode* head) {
ListNode *slow = head, *fast = head,tmp=head;
while (fast && fast->next) {
tmp=slow;
slow = slow->next;
fast = fast->next->next;
}
tmp->next = NULL; // 切断链表 [head, slow] [slow->next, 末尾]
return slow;
}
// 合并两个有序链表
ListNode* merge(ListNode *L1, ListNode *L2) {
if (!L1) return L2;
if (!L2) return L1;
ListNode dummy;
ListNode *p = L1, *q = L2, *cur = &dummy;
while (p && q) {
ListNode *newNode = NULL;
if (p->val < q->val) {
newNode = p;
p = p->next;
} else {
newNode = q;
q = q->next;
}
cur->next = newNode;
cur = cur->next;
}
if (p) cur->next = p;
if (q) cur->next = q;
return dummy.next;
}
ListNode* sortList(ListNode* head){
if(!head || !head->next) // 归并排序递归条件: 至少2个节点
return head;
ListNode* mid = getMid(head); // 将链表切成2半:[head, mid] [mid->next, 尾部节点]
ListNode* L1 = sortList(head); // 左递归(划分):[head, NULL)
ListNode* L2 = sortList(mid); // 右递归(划分):[mid, NULL)
return merge(L1, L2); // 合并:[left, mid] [mid->next, right]
}
};
表达式求值
简介:实现对正整数的加减乘除四种操作和括号运算
思路:有两个栈,一个栈用来存储运算符op,一个栈用来存储数字nums,读入运算符时碰到优先级低的运算符
下面举个例子来看一下具体是怎么实现的
- 2+3*4
- 2+2*3-4/2
- 1-1-1 23232*43+3443
- (2+2)*(4/2)
#include<iostream>
#include<stack>
#include<algorithm>
#include<unordered_map>
using namespace std;
stack<int>nums;
stack<int>op;
unordered_map<char,int>m;
void eval(){
int b=nums.top();
nums.pop();
int a=nums.top();
nums.pop();
char c=op.top();
op.pop();
if(c=='+')nums.push(a+b);
else if(c=='-')nums.push(a-b);
else if(c=='*')nums.push(a*b);
else nums.push(a/b);
}
123231+
int main(){
string s;
cin>>s;
m['+']=1;m['-']=1;
m['*']=2;m['/']=2;
for(int i=0;i<s.size();i++){
if(s[i]>='0'&&s[i]<='9'){
int j=i;
int num=0;
while(j<s.size()&&s[j]>='0'&&s[j]<='9'){
num=num*10+(s[j++]-'0');
}
i=j-1;
nums.push(num);
}else if(s[i]=='('){
op.push('(');
}
else if(s[i]==')'){
while(op.top()!='(')eval();
op.pop();
}else{
char c=s[i];
while(!op.empty()&&m[op.top()]>=m[c])eval();
op.push(c);
}
}
while(op.size())eval();
cout<<nums.top()<<endl;
}
最小栈
问题:实现一个栈的数据结构,有三种基本操作,插入栈,弹出栈,求出栈中的元素的最小值。
https://leetcode-cn.com/problems/min-stack/
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
if (st.size() == 0) {
st.push({x, x});
} else {
st.push({x, min(x, st.top().second)});
}
}
void pop() {
st.pop();
}
int top() {
return st.top().first;
}
int getMin() {
return st.top().second;
}
private:
stack<pair<int, int>> st;
};
01背包
4个物品,背包的体积为8,选出最大价值
体积 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
价值 | 2 | 4 | 4 | 5 |
打的表
体积/物品 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
2 | 0 | 2 | 4 | 6 | 6 | 6 | 6 | 6 | 6 |
3 | 0 | 2 | 4 | 6 | 6 | 8 | 10 | 10 | 10 |
4 | 0 | 2 | 4 | 6 | 6 | 8 | 10 | 11 | 11 |
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010,M=1010;
int dp[N][M];
int n,v;
int main(){
cin>>n>>v;
for(int i=1;i<=n;i++){
int a,b;
cin>>a>>b;
for(int j=0;j<=v;j++){
if(j>=a)dp[i][j]=max(dp[i-1][j],dp[i-1][j-a]+b);
else dp[i][j]=dp[i-1][j];
}
}
cout<<dp[n][v];
return 0;
}
纸牌博弈
样例
输入:
4
1 2 100 4
输出:
101
输入:
5
1 2 100 4 2
输出:
101
暴力递归
函数fun1(int start,int end)代表绝顶聪明的人在局面为(start,end)先手会做出什么决策
函数fun2(int start,int end)代表绝顶聪明的人在局面为(start,end)后手会做出什么决策
#include<iostream>
using namespace std;
const int N=5010;
int arr[N];
int n;
int fun2(int start,int end);
int fun1(int start,int end){
if(start==end)return arr[start];
int a=arr[start]+fun2(start+1,end);
int b=arr[end]+fun2(start,end-1);
return max(a,b);
}
int fun2(int start,int end){
if(start==end)return 0;
//后手的两种情况
int a=fun1(start+1,end);
int b=fun1(start, end-1);
return min(a,b);
}
int main(){
cin>>n;
for(int i=0;i<n;i++)cin>>arr[i];
int a=fun1(0,n-1);
int b=fun2(0,n-1);
cout<<max(a,b);
return 0;
}
备忘录优化
#include<iostream>
#include<cstring>
using namespace std;
const int N=5000;
int arr[N];
int f1[N][N],f2[N][N];
int n;
int fun2(int start,int end);
int fun1(int start,int end){
if(f1[start][end]!=-1)return f1[start][end];
if(start==end) f1[start][end]= arr[start];
else{
int a=arr[start]+fun2(start+1,end);
int b=arr[end]+fun2(start,end-1);
f1[start][end]=max(a,b);
}
return f1[start][end];
}
int fun2(int start,int end){
if(f2[start][end]!=-1)return f2[start][end];
if(start==end)f2[start][end]=0;
else{
int a=fun1(start+1,end);
int b=fun1(start, end-1);
f2[start][end]=min(a,b);
}
return f2[start][end];
}
int main(){
cin>>n;
for(int i=0;i<n;i++)cin>>arr[i];
memset(f1,-1,sizeof f1);
memset(f2,-1,sizeof f2);
int a=fun1(0,n-1);
int b=fun2(0,n-1);
cout<<max(a,b);
return 0;
}
补充:博弈问题常见的先手胜负问题
#include<iostream>
using namespace std;
const int N=5010;
int arr[N];
int n;
bool fun(int start,int end,int a,int b){
if(start==end)return a+arr[start]>b;
if(!fun(start+1,end,b,a+arr[start])||!fun(start,end-1,b,a+arr[end]))return true;
return false;
}
int main(){
cin>>n;
for(int i=0;i<n;i++)cin>>arr[i];
bool flag=fun(0,n-1,0,0);
cout<<flag;
}
捡石头
二维数组旋转打印
class Solution {
public void rotate(int[][] matrix) {
int l = 0;// top left
int r = matrix.length - 1;// low right
while (r > l) {
fun(l,r,matrix);
r--;
l++;
}
}
void fun(int l,int r,int[][] matrix){
for (int i = l; i < r; i++) {
int temp = matrix[l][i];
matrix[l][i] = matrix[r - (i - l)][l];
matrix[r - (i - l)][l] = matrix[r][r - (i - l)];
matrix[r][r - (i - l)] = matrix[l + (i - l)][r];
matrix[l + (i - l)][r] = temp;
}
}
}
螺旋矩阵
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int rc=0;
int r2=matrix.length-1;
int c2=matrix[0].length-1;
ArrayList<Integer>res=new ArrayList();
while(r2>=rc&&c2>=rc){
fun(rc,r2,c2,matrix,res);
rc++;r2--;c2--;
}
return res;
}
void fun(int rc,int r2,int c2,int[][] matrix,ArrayList<Integer>res){
if(rc==r2&&rc==c2){
res.add(matrix[rc][rc]);
return;
}
if(rc==r2){
for(int i=rc;i<=c2;i++)res.add(matrix[rc][i]);
return;
}
if(rc==c2){
for(int i=rc;i<=r2;i++)res.add(matrix[i][c2]);
return;
}
for(int i=rc;i<c2;i++)res.add(matrix[rc][i]);
for(int i=rc;i<r2;i++)res.add(matrix[i][c2]);
for(int i=c2;i>rc;i--)res.add(matrix[r2][i]);
for(int i=r2;i>rc;i--)res.add(matrix[i][rc]);
}
}
字符串查找
有两个字符串,子串/母串
子串=abc
母串=dabef(cba)kkk
让在母串中,找到子串第一次匹配位置
注解:子串的位置可以变化
要求时间复杂度o(n)
//主要是将判断的时间复杂度化为常数
求超过1/2的元素
思路:关键在于相互抵消之后存在的元素就是结果(两个互相不相同的元素可以相互抵消)
class Solution {
public int majorityElement(int[] nums) {
int cnt=1;
int num=nums[0];
for(int i=1;i<nums.length;i++){
if(nums[i]==num)cnt++;
else {
if(cnt==0){
cnt=1;
num=nums[i];
}else cnt--;
}
}
return num;
}
}
补充:求超过n/3次的元素