山东大学计算机科学与技术学院程序设计思维与实践作业 week12- 动态规划(2)

山东大学计算机科学与技术学院程序设计思维与实践作业
山大程序设计思维与实践作业
sdu程序设计思维与实践
山东大学程序设计思维实践作业H
山大程序设计思维实践作业H
山东大学程序设计思维与实践
动态规划
week12- 动态规划(2)

相关资料:GitHub

A : 01背包

题目描述
有 N 件物品和一个容量为 V 的背包。第 i 件物品的重量是 w[i],价值是 c[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入描述
第一行为 N(1≤N≤5000),V(1≤V≤5000)。
下面 N 行,第 i 行描述第 i 个物品的 wi,ci,用一个空格分隔。

输出描述
输出只有一个数,最大总价值。

测试样例
样例 1
输入:
10 9
7 1
8 10
7 10
7 7
7 6
3 7
4 1
3 3
9 1
1 4

输出:
14

#include<bits/stdc++.h>
using namespace std;
#define mxnum 6000
int w[mxnum]={0},c[mxnum]={0},f[mxnum]={0};
int main()
{
	int result=0;
	int n,v;
	cin>>n>>v;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i];
		cin>>c[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=v;j>=1;j--)
	{
		if(j>=w[i])
		{
			f[j]=max(f[j],f[j-w[i]]+c[i]);
		}
	}
	}
	result=f[v];
	cout<<result<<endl;
	return 0;
}

B : 完全背包

题目描述
有 N 种物品(每种有无限多个)和一个容量为 V 的背包。第 i 种物品的重量是 w[i],价值是 c[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入描述
第一行为 N(1≤N≤5000),V(1≤V≤5000)。
下面 N 行,第 i 行描述第 i 种物品的 wi,ci,用一个空格分隔。

输出描述
输出只有一个数,最大总价值。

测试样例
样例 1
输入:
10 8
4 5
1 1
1 5
5 4
5 10
6 5
1 1
5 6
7 4
4 5

输出:
40

#include<bits/stdc++.h>
using namespace  std;

int n;
int v;
long long result;
int w[5005];
int c[5005];
int k[5005][5005];


int main(){
    cin>>n>>v;
    for(int i=1;i<=n;i++){
        cin>>w[i]>>c[i];
    }
    //it will find which one can put first
    for(int i=1;i<=n;i++){
        for(int j=1;j<=v;j++){
            k[i][j] = k[i-1][j];
            if(w[i] <= j){
                k[i][j] = max(k[i-1][j],k[i][j-w[i]] + c[i]);
                
            }
        }
        
    }
    cout<<k[n][v];
}

C : 多重背包

题目描述
有 N 种物品和一个容量为 V 的背包。第 i 种物品的重量是 w[i],价值是 c[i],有 k[i]个。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入描述
第一行为 N(1≤N≤104),V(1≤V≤104)。
下面 N 行,第 i 行描述第 i 种物品的 wi,ci,ki,用一个空格分隔。

输出描述
输出只有一个数,最大总价值。

测试样例
样例 1
输入:
6 1
7 10 4
6 9 1
6 8 1
10 6 2
9 8 3
1 3 2

输出:
3

#include <bits/stdc++.h>
using namespace std;
long long w[100005];
long long c[100005];
long long f[100005];
int now = 0;
void addItem(long long a,long long b){
    now++;
    w[now] = a;
    c[now] = b;
}
int main(){
    int N,V;
    scanf("%d %d",&N,&V);
    while(N--){
        long long wt,vlu,cnt;  //weight,value,count
        cin>>wt>>vlu>>cnt;
        for(int i = 1;i<=cnt;i*=2){
            addItem(wt*i,vlu*i);
            cnt-=i;
        }
        if(cnt>0){
            addItem(wt*cnt,vlu*cnt);
        }
    }
    for(int i = 1;i<=V;i++){
        f[i] = 0;
    }
    for(int i = 1;i<=now;i++){
        for(int j = V;j>=1;j--){
            if(j-w[i]>=0) f[j] = max(f[j],f[j-w[i]]+c[i]);
        }
    }
    printf("%lld\n",f[V]);
    return 0;
}

D : 分组背包

题目描述
有 N 件物品和一个容量为 V 的背包,将所有的物品划分成若干组,每个组里面的物品最多选一件。第 i 件物品的重量是 w[i],价值是 c[i],属于组 k[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入描述
第一行为 N(1≤N≤103),V(1≤V≤103)。
下面 N 行,第 i 行描述第 i 个物品的 wi,ci,ki,用一个空格分隔。

输出描述
输出只有一个数,最大总价值。

测试样例
样例 1
输入:
7 9
6 6 3
5 10 3
3 5 1
9 4 4
6 1 4
8 3 5
5 2 2

输出:
15

#include<bits/stdc++.h>
using namespace std;
#define mxNUM 1005
int N,V;
long long dp[mxNUM];
vector<pair<int,long long> > vl[103];
int main(){
    scanf("%d%d",&N,&V);
    int x;
    long long y;
    int z;
    int ts = 0; //记录组数
    for(int i = 0; i < 103; i++)
        vl[i].emplace_back(0,0);
    for(int i = 1; i <= N; i++){
        scanf("%d%lld%d",&x,&y,&z);
        vl[z].emplace_back(x,y);
        ts = max(z,ts);
    }
    for(int k = 1; k <= ts; k++){
        for(int i = V; i >= 0; i--)
            for(int j = 1; j < vl[k].size(); j++)
                if(i >= vl[k][j].first)
                    dp[i] = max(dp[i],dp[i-vl[k][j].first] + vl[k][j].second);

    }
    printf("%lld",dp[V]);
    return 0;
}

E : 超大背包

题目描述
有 N 件物品和一个容量为 V 的背包。第 i 件物品的重量是 w[i],价值是 c[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。

输入描述
第一行为 N(1≤N≤40),V(1≤V≤1015)。
下面 N 行,第 i 行描述第 i 个物品的 wi,ci,用一个空格分隔。

输出描述
输出只有一个数,最大总价值。

测试样例
样例 1
输入:
3 225274242
70498827 830583485
72910089 759360759
80945586 1095298545

输出:
2685242789

样例 2
输入:
27 1405406868
500580317 1559925714
1191095816 2052289019
2086671060 125049457
1467200227 1826963529
1054830291 1055178046
457445390 293196664
1428828824 1163887408
27108927 353186119
354284919 1641947343
1044113045 1319050872
814300917 42212882
802287458 2142953934
1178508095 943859578
2133693898 163905627
1687820729 1091482274
737249638 2121489517
1203304272 1081378899
581034126 832395967
1370524005 487337507
178833685 1328104836
512934928 897959032
1629846549 1677014752
2011536830 64430283
547991487 1683026867
2125422226 728351378
744805340 261154211
1909716650 317131682

#include <bits/stdc++.h>
#define ll long long
using namespace std;
vector<pair<ll, ll>> wc1, wc2, subs1, subs2;

// 初始化子集
void init(vector<pair<ll, ll>> &wc, vector<pair<ll, ll>> &sub) {
    // 对于wc.size个数,需要size个0/1来表示选择与否,转化成常数就是1到2^size-1
    ll tmp = pow(2, wc.size()) - 1;
    for (ll i = 0; i <= tmp; i++) { // 01枚举子集选择方案
        sub.push_back({0, 0});
        for (int j = 0; j < wc.size(); j++) {
            // 位运算,对于第j个物品是否选择,即看(i>>j)&1是否为1
            if ((i >> j) & 1 == 1) {
                sub.back().first += wc[j].first;
                sub.back().second += wc[j].second;
            }
        }
    }
}

int main() {
    ll n, v;
    cin >> n >> v;
    ll w, c;
    for (ll i = 1; i <= n / 2; i++) {
        cin >> w >> c;
        wc1.push_back({w, c});
    }
    for (int i = 1; i <= n - n / 2; i++) {
        cin >> w >> c;
        wc2.push_back({w, c});
    }
    // 获取所有子集到sub
    init(wc1, subs1);
    init(wc2, subs2);
    // 删除不符合条件的元素
    // 先对子集排序,对于first排序好后比较second,如果后一个second比前一个大就是不按序,
    // 这时候不更新,下一次找到按序的填补到这个位置,即将所有按序的元素全部紧凑排列,非按序的将被替换
    sort(subs1.begin(), subs1.end());
    sort(subs2.begin(), subs2.end());
    ll idx1 = 1, idx2 = 1;
    for (ll i = 1; i < subs1.size(); i++) {
        if (subs1[idx1 - 1].second < subs1[i].second) {
            subs1[idx1++] = subs1[i];
        }
    }
    for (ll i = 1; i < subs2.size(); i++) {
        if (subs2[idx2 - 1].second < subs2[i].second) {
            subs2[idx2++] = subs2[i];
        }
    }
    // 遍历子集1每个元素,找到体积范围内价值最大的元素,计算最大价值
    ll result = LONG_LONG_MIN;
    for (ll i = 0; i < idx1; i++) {
        // 查找元素,upper_bound找到第一个大于体积限定的元素,it--为目标
        ll it = upper_bound(subs2.begin(), subs2.begin() + idx2,
                            make_pair(v - subs1[i].first, LONG_LONG_MAX)) -
                subs2.begin(); 
        it--;
        // 体积符合的情况下,计算最大价值
        if (subs2[it].first + subs1[i].first <= v) {
            result = max(result, subs2[it].second + subs1[i].second);
        }
    }
    cout << result;
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于51单片机的简单万年历程序设计的代码: ``` #include<reg52.h> #include<intrins.h> typedef unsigned char uchar; typedef unsigned int uint; uchar code table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴数码管0~9 a~f uchar year, month, day; uchar week; //定义年月日和星期 uchar dis_1,dis_2,dis_3,dis_4; //定义四个数码管的显示值 void delay(uint z) //延时函数 { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } void get_time() //获取时间函数 { uchar temp; temp = DS1302_Read(0x86); year = DS1302_Read(0x86); month = DS1302_Read(0x88); day = DS1302_Read(0x8A); week = DS1302_Read(0x8C); year = year + 2000; } void main() { TMOD = 0x01; //定时器0工作模式,16位计数 TH0 = 0xFA; //赋初值 TL0 = 0xCD; TR0 = 1; //开启定时器0 ET0 = 1; //开启定时器0中断 EA = 1; //开启总中断 while(1) { get_time(); //获取时间 dis_1 = year / 1000; //计算千位数码管的值 dis_2 = (year % 1000) / 100; //计算百位数码管的值 dis_3 = (year % 100) / 10; //计算十位数码管的值 dis_4 = year % 10; //计算个位数码管的值 P2 = 0x00; //清空P2口 P0 = table[dis_1]; //显示千位数码管的值 P2 = 0x01; //P2.0口输出高电平,点亮千位数码管 delay(2); //延时一段时间,防止闪烁 P2 = 0x00; P0 = table[dis_2]; P2 = 0x02; delay(2); P2 = 0x00; P0 = table[dis_3]; P2 = 0x04; delay(2); P2 = 0x00; P0 = table[dis_4]; P2 = 0x08; delay(2); } } void T0_time() interrupt 1 //定时器0中断函数 { TH0 = 0xFA; //重置定时器0初值 TL0 = 0xCD; DS1302_Init(); //DS1302初始化 } ``` 该代码使用了DS1302实时时钟芯片来获取时间。具体的接口和初始化函数在代码中没有给出,需要自行添加。在主函数中,通过计算年份的千位、百位、十位和个位数码管的值来实现万年历的显示效果。在定时器0中断函数中,调用了DS1302的初始化函数来更新时间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值