import java.util.ArrayList;
import java.util.Scanner;
public class demo19 {
/* 0 1 背包问题
有 N件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
4 5
1 2
2 4
3 4
4 5
* */
static int n,V;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n= sc.nextInt();
V=sc.nextInt();
int []v=new int[n+1];//从1~n
int []w=new int[n+1];
int []s=new int[n+1];
for (int i = 1; i <=n; i++) {
v[i]=sc.nextInt();
w[i]=sc.nextInt();
s[i]=sc.nextInt();
}
//zeroOne(n,V,v,w);
//Entire(n,V,v,w);
// Multiple(n,V,v,w,s);
MultipleImprove(n,V,v,w,s);
}
static void zeroOne(int n,int V,int v[],int w[]){
int dp[][]=new int[n+1][V+1];//dp[i][j]表示从前i个元素,背包体积为j时选取的价值最大值
for (int i = 1; i <=n ; i++) {
for (int j = 1; j <=V ; j++) {
if(v[i]>j)dp[i][j]=dp[i-1][j];
else dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}
}
System.out.println(dp[n][V]);
}
//完全背包:每种物品的个数无限可用
static void Entire(int n,int V,int v[],int w[]){
int dp[][]=new int[n+1][V+1];//这里是为了防止数组索引越界
for (int i = 1; i <=n ; i++) {
for (int j = 1; j <=V ; j++) {
if(v[i]>j)dp[i][j]=dp[i-1][j];
/* else for (int k = 0; k*v[i]<=j ; k++) {
dp[i][j]=Math.max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}*/
//经过数学公式推导进行化简可以简化得到以下式子
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
}
}
System.out.println(dp[n][V]);
}
//多重背包1 每种物品的数目有限 定义一个数组s[i]来存储物品的数目
static void Multiple(int n,int V,int v[],int w[],int s[]){
int dp[][]=new int[n+1][V+1];
//i表示现在取到了第i种物品
for (int i = 1; i <=n ; i++) {
//j表示现在背包的容量
for (int j = 1; j <=V ; j++) {
if(v[i]>j)dp[i][j]=dp[i-1][j];
//k表示现在取的第i种物品的数目
else for (int k = 0; k*v[i]<=j&&k<=s[i] ; k++) {
dp[i][j]=Math.max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
System.out.println(dp[n][V]);
}
/*
多重背包 的优化 使用二进制优化
我们知道一个十进制数字可以用二进制数来表示,这里我们将物品的数目拆分为二进制的
例如 8可以由2^0+2^1+2^2+1
因为物品数目可数,所以可以将其转化为01背包问题
我们只需要对原来数组进行拆分即可 列如:
物品1 体积2 价值3 数量7 7=1+2+4可以变化为 14=1+2+4+7
1 物品1.1 体积2 价值3 数量1
2 物品1.2 体积2*2 价值3*2 数量1
4 物品1.3 体积2*4 价值3*4 数量1
注意 若物品数量为8 则8=1+2+4+1;
原来的7次比较就变为了3次比较
* */
static void MultipleImprove(int n,int V,int v[],int w[],int s[]){
//现在对物品进行拆分,因为不知道拆分完后共有多少个物品,这里选择利用list集合,需要定义一个类
ArrayList<goods> list=new ArrayList<>();
list.add(new goods(0,0));//将第一个元素设为0,防止后续操作index溢出
for (int i = 1; i <=n ; i++) {
for (int k = 1;k<s[i]; k*=2) {
list.add(new goods(k*v[i],k*w[i]));
s[i]-=k;
}
if(s[i]>0)list.add(new goods(s[i]*v[i],s[i]*w[i]));
}
//接下来就是01背包问题
int l=list.size();
int dp[][]=new int[l][V+1];
//i表示现在取到了第i种物品
for (int i = 1; i <=l-1 ; i++) {
for (int j = 1; j <=V ; j++) {
if(list.get(i).getV()>j)dp[i][j]=dp[i-1][j];
else dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-list.get(i).getV()]+list.get(i).getW());
}
}
System.out.println(dp[l-1][V]);
}
static class goods{
private int v;
private int w;
public goods(int v, int w) {
this.v = v;
this.w = w;
}
public int getV(){
return this.v;
}
public int getW(){
return this.w;
}
}
}
import java.util.Scanner;
public class demo20 {
/*
* 数位dp
* 小蓝最近在学习二进制。他想知道 1到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗?
对于所有评测用例,1≤N≤10^18,1≤K≤50
* 思路:1.判断该题目是数位dp
* 2.设计状体
* 3.套用模板 dfs(int pos,(state),boolean lead,boolean limit)pos:当前位置 state:状态自己寻找 lead:前导0影响 limit:是否限制
* */
//2^60=1,152,921,504,606,846,976 19位 一般取值要大一点 避免发生未知的错误
static long dp[][]=new long[65][65];//dp[pos][state] 表示在pos位满足状态的数目
static long n;
static int k;
static int num[]=new int[65];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextLong();
k= sc.nextInt();
System.out.println(solve(n, k));
}
static long dfs(int pos,int sum,boolean lead,boolean limit){
if(pos==0)return sum==k?1:0;//边界条件
if(dp[pos][sum]!=-1&&!limit&&!lead)return dp[pos][sum];//如果没有限制并且没有前导0返回前面已经存储过的值
int up=limit?num[pos] :1;// 寻找上限值
long res=0;
for (int i = 0; i <=up ; i++) {
//有时候有前导0需要剪枝处理
res += dfs(pos-1, sum + (i == 1 ? 1 : 0), lead && i == 0, limit &&i == num[pos]);
}
if(!limit&&!lead) dp[pos][sum]=res;//如果没有限制并且没有前导0存储当前结果
return res;
}
static long solve(long n,int k){
//先将dp[i][sum]初始化为-1;
for (int i = 0; i < dp.length ; i++) {
for (int j = 0; j < dp.length; j++) {
dp[i][j]=-1;
}
}
//将n转化为二进制
int pos=0;
while (n>0){
num[++pos]=(int)(n%2);
n/=2;
}
return dfs(pos,0,true,true);
}
}
import java.util.Scanner;
public class demo25 {
//manacher算法
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String s= sc.next();
manacher(s);
}
/*
* '$'是为了设置俩个哨兵防止数组索引越界
* '#'在原来的数组中前一个和最后一个以及元素之间插入'#'让数组长度变为奇数个。
* l,r是盒子的区间
* d[i]是该位置最大的匹配长度
* 1.如果i在盒子里面i<r那么d[i]=min(d[l+r-i],r-i)
* 2.如果i不在盒子里面则暴力求解while(arr[i-d[i]]==arr[i+d[i]])d[i]++;
* 3.如果l+d[i]>=r更新盒子区间l=i-d[i]+1,r=i+d[i]-1,
* */
static void manacher(String s){
int ll=s.length();
int n=2*ll+1+2;
char []arr=new char[n];
int []d=new int[n];
d[1]=1;
int pos=0;
arr[pos++]='$';
arr[pos++]='#';
for (int i = 0; i < ll; i++) {
arr[pos++]=s.charAt(i);
arr[pos++]='#';
}
arr[pos]='$';
for (int i = 2,l=0,r=1; i <=n-2 ; i++) {
if(i<=r)d[i]=Math.min(d[l+r-i],r-i+1);
while(arr[i-d[i]]==arr[i+d[i]])d[i]++;
if(d[i]>r){l=i-d[i]+1;r=i+d[i]-1;}
}
for (int i = 1; i <=n-2; i++) {
if(d[n-1]<d[i])d[n-1]=d[i];
}
System.out.println(d[n-1]-1);
}
}