2025/1/27课堂记录

目录

  1. 旅行计划
  2. Banknotes

这次优化了一下代码

c:3 1 5 0 5

d:1 2 2 1 4

s1:2 -1 3 -1 1 2 -1 3 -1 1

s2:2 1 4 3 4 6 5 8 7 8

s2[i]=s1[i]+s2[i-1];

s2[1]=c1-d1

s2[2]=c2-d2+c1-d1

s2[3]=c3-d3+c2-d2+c1-d1

s2[3]-s2[1]=c3-d3+c2-d2+c1-d1-c1+d1=c3-d3+c2-d2,即从2走到4(4不加油)后还剩多少油

然后就看注释吧,精华都在注释里
#include <iostream>
using namespace std;
long long o[1000200],d[1000200],s[1000200],q[1000200],vis[1000200];
int main()
{
	
	int n;
    cin>>n;
    for(long long  i=1;i<=n;i++)cin>>o[i]>>d[i];
	
    for(long long i=1;i<=n;i++)s[i+n]=s[i]=o[i]-d[i];
    for(long long i=1;i<=2*n;i++)s[i]+=s[i-1];
    long long hh=0,tt=0;
    q[0]=2*n+1,tt=1;//补“0 ” 
    for(long long i=2*n;i>=0;i--)
    {
        while(hh<tt&&q[hh]>i+n)hh++;//i是起点,i+n是q[hh]<=i+n是一圈之内; 
        if(i<n)//i作为出发点 
            if(s[q[hh]]-s[i]>=0)
				vis[i+1]=1;
//因为在循环执行到i>=n这个期间,维护单调递增,s[q[hh]]永远最小,即走到这个点剩余油量最小,那么s[q[hh]]-s[i]永远最小
//即从i+1走到q[hh],剩余油量最小。如果这个都>=0,那在i+1走到i+n+1这一圈以内就不会出现负数了,即i+1可以作为起点 
        while (hh<tt&&s[q[tt-1]]>=s[i]) tt--;//维护s[q[hh]]永远最小 
        q[tt++]=i;
    }
    
    d[0]=d[n];
    for(long long i=1;i<=n;i++)s[i+n]=s[i]=o[i]-d[i-1];
    for(long long i=1;i<=2*n;i++)s[i]+=s[i-1];
    hh=0,tt=0;
    q[0]=0,tt=1;//补0 
    for(long long i=1;i<=2*n;i++)
    {
        while(hh<tt&&q[hh]<i-n)hh++;
        if(i>n)
            if(s[i]-s[q[hh]]>=0)
				vis[i-n]=1;
        while(hh<tt&&s[q[tt-1]]<=s[i])tt--;//单调递减 
        q[tt++]=i;
    }
 
    for(long long i=1;i<=n;i++)
    {
        if(vis[i])cout<<"TAK\n";
        else cout<<"NIE\n";
    } 
    return 0;
}

这是一道多重背包题,即每个物品有有限个(01:一个,完全:无限个)

当然,你可以直接套用多重背包模板,如:

只使用模板
 #include<bits/stdc++.h>
using namespace std;
int b[105],c[105],f[105][105];//f[i][j]:只使用前i种货币,凑够面值为j的钱,总共要用几枚硬币 
int main()
{
    int m,n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>b[i];
    for(int i=1;i<=n;i++)cin>>c[i];
    cin>>m;
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++)//枚举每种硬币的到来 
        for(int j=0;j<=m;j++)//枚举每种面值 
            for(int k=0;k<=c[i];k++)//枚举这种硬币所用的枚数 
                if(j-k*b[i]>=0)//不越界 
                    f[i][j]=min(f[i][j],f[i-1][j-k*b[i]]+k);//把之前所用的i-1种硬币
    cout<<f[n][m];/
    return 0;
}

然后,你会发现他的时间复杂度确实太高了一点,3for,数据大了就见祖宗

有什么办法降低时间复杂度呢?

你会想到:01背包和完全背包只用2for,能不能把多重背包变成他俩呢?

把有限个变成无限个,显然太离谱

所以可以变成01背包

于是,便有了下面这种方法:把好几枚硬币摞在一块,体重,单价,数量都翻倍

这个是这种方法的模板,拆完再跑
 #include<bits/stdc++.h>
using namespace std;
int w[59],c[59],v[59],f[59][59];
int main()
{
	int n,we,co,va,s=0,V;
	cin>>V>>n;
	
	for(int i=1;i<=n;i++)
	{
		cin>>we>>va>>co;
		//   体重  单价   数量 
		//   把一种物体的数量二进制分解成若干种"小物体" 
		for(int j=1;j<=co;j<<=1)  // j=j*2;   j=j<<1; 
		{
			w[++s]=j*we;          // s统计小物体的个数 
			v[s]=j*va;
			co-=j;
		}
		if(co>0)
		{
			w[++s]=co*we;
			v[s]=co*va;
		}
	}
	
	//  做01背包 
	for(int i=1;i<=s;i++)   //新的种类 
		for(int j=1;j<=V;j++)  // 总体积 
			if(j>=w[i])
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
			else
				f[i][j]=f[i-1][j];
				
	cout<<f[s][V];
	return 0;
}

 

这个是这道题的解法,但是是边拆边跑
 //https://www.cnblogs.com/GXZlegend/p/7434553.html
//Bank Notes 多次背包实现 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int b[210],c[210],f[20010],m,n;
void dp(int w,int c)
{
    for(int i=m;i>=w;i--) 
	    f[i]=min(f[i],f[i-w]+c);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
	    scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)
	    scanf("%d",&c[i]);
    scanf("%d",&m);
    memset(f,0x3f,sizeof(f)),f[0]=0;
    for(int i=1;i<=n;i++)
    {
    	//将c[i]拆成2^0,2^1,2^2.......2^k,(c[i]-s)
        for(int j=1;j<=c[i];j<<=1)
		    dp(b[i]*j,j),c[i]-=j;
        if(c[i])dp(b[i]*c[i],c[i]);
    }
    printf("%d\n",f[m]);
    return 0;
}

还有一种方法——既然是dp,那么肯定有专属的优化方法,而多重背包也可以用单调队列优化!!

具体的话,还是看一看这篇博客吧,其实我也没大弄明白

我这个是错误代码,没改过来,这坑以后补吧
 #include<iostream>
#include<cstring>
using namespace std;
int b[1000],c[1000],w[20000],v[20000],f[20000][20000];
int main()
{
	int n,k,m=0;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>b[i];
	for(int i=1;i<=n;i++)cin>>c[i];
	cin>>k;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=c[i];j+=j)
		{
			w[++m]=j;//枚数 
			v[m]=j*b[i];//面值 
			c[i]-=j;
		}
		if(c[i]>0)
		{
			w[++m]=c[i];
			v[m]=c[i]*b[i];
		}
	}
	for(int i=1;i<=m;i++)cout<<w[i]<<" "<<v[i]<<"\n";
	for(int i=1;i<=m;i++)f[1][i]=0x3f3f3f3f;
	for(int i=1;i<=m;i++)//种类 
		for(int j=1;j<=k;j++)//面值 
			if(j>=k*v[i])
				f[i][j]=min(f[i-1][j],f[i-1][j-v[i]]+w[i]);
			else
				f[i][j]=f[i-1][j]; 
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=k;j++)
			cout<<f[i][j];
		cout<<"\n";
	}
	cout<<f[m][k];
	return 0;
}

我这坑是越来越多了

posted @ 2025-02-07 20:15  永韶  阅读(10)  评论(0编辑  收藏  举报