5.11校内测试
Elevator
输入文件 | 输出文件 |
---|---|
Elevator.in | Elevator.out |
【题目描述】(Elevator.cpp/c/pas)
信息大佬们想建造太空梯(用魔法石垒)进入太空。他们有k (1 ≤K ≤ 400)种不同类型的魔法石,每一种魔法石的高度为h(1 ≤ h≤100),数量为c (1 ≤ c ≤10),由于会受到太空辐射而失去魔力,每一种魔法石不能超过这种魔法石的最大建造高度a (1≤ a≤40000),试求利用这些魔法石所能修建的太空梯的最高高度。
【输入格式】
第一行为一个整数即k。第2行到第k+1行每一行有三个数,代表每种类型魔法石的特征,即高度h,限制高度a和数量c。
【输出格式】
一个整数,即修建太空梯的最大高度。
【输入样例】 【输出样例】
3 48
7 40 3
5 23 8
2 52 6
【样例说明】
15+21+12
最底下为3块石头2型,中间为3块石头1型,上面为6块石头3型。放置4块石头2型和3块石头1型是不可以的,因为顶端的石头1型的高度超过了40的限制。
分析:
根据所给的样例,发现全贪心是不可的,具有后效性,需用DP解决。
本质为多重背包,将每个类型的石头,以最高限制高度由小到大排序(如果先盖最高限制大的,最高限制小的可能盖不了) (部分贪心)
多重背包转01背包 ----好写又好想 ,01背包不香吗
简单来说,本题的物品数量有上限,将第 \(i\) 种物品都分为对应数量的01背包的物品,得到数量为 \(\sum num\) 的01背包,按01背包做法即可。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<memory.h>
using namespace std;
struct Node
{
int c,h,a;
}node[402];
int dp[40010];
void clear()
{
memset(dp,0,sizeof(dp));
memset(node,0,sizeof(node));
}
bool cmp(Node x,Node y)
{
return x.a<y.a;
}
int main()
{
int n;
while(scanf("%d",&n)==1)
{
clear();
for(int i=1;i<=n;i++)
scanf("%d %d %d",&node[i].h,&node[i].a,&node[i].c);
sort(node+1,node+n+1,cmp);
int maxn=0;
for(int i=1;i<=n;i++)//n种物品
{
for(int k=1;k<=node[i].c;k++)//物品数量受限
{
for(int j=node[i].a;j>=node[i].h;j--)//简单的01背包
{
dp[j]=max(dp[j],dp[j-node[i].h]+node[i].h);
if(maxn<dp[j]) maxn=dp[j];
}
}
}
printf("%d\n",maxn);
}
return 0;
}
游戏
游戏(game.cpp, 64MB, 1秒)
【问题描述】
两个大佬Stan和Ollie正在玩一种数字游戏。给定两个正整数M和N,从Stan开始,取其中较大的一个数,减去较小的数的正整数倍,当然,得到的数K不能小于0。然后是Ollie,对刚才得到的数K,和M,N中较小的那个数,再进行同样的操作,…直到一个人得到了0,他就取得了胜利。下面是他们用(25,7)两个数游戏的过程:
Start: 25 7
Stan: 11 7 { 18 7, 11 7 , 4 7 均可能}
Ollie: 4 7
Stan: 4 3
Ollie: 1 3
Stan: 1 0
Stan赢得了游戏的胜利。
现在,假设他们“完美”地操作,谁会取得胜利呢?
【输入格式】
第一行为测试数据的组数C。
下面有C行,每行为一组数据,包含两个正整数M和N, M和N的范围不超过长整型。
【输出格式】
对每组输入数据输出一行。
如果Stan胜利,则输出“Stan wins”;否则输出“Ollie wins”。
【输入和输出样例】
game.in | Game.out |
---|---|
2 | Stan wins |
25 7 | Ollie wins |
24 15 |
题目分析:
打眼一看,和 GCD 貌似有些关系(雾,牵扯到了博弈论(这不重要),游戏的结束是直到一方为0,我们除了这能提前预判吗?
如果 $a/b=1 $ 的话,那么无论如何我们都只有一种方案 , 把a中拿去b个
而拿走以后并不能直接判断出来,所以需要继续递推,此时轮到另一个人摸石头,注意状态要反着标
如果\(b=0\),那么先手必胜,即当前操作人失败。
如果\(a/b>1\) , 先手具有主动权, 他可以决定对方的选择(在“完美”操作下),先手必胜
以样例为例
状态 | 减一倍 | 减两倍 | 减三倍 |
---|---|---|---|
25 7 | 25 7 | 25 7 | |
S | 18 7 | 11 7 | 4 7 |
O | 11 7 | 4 7 | 4 7 | 4 3 |
S | 4 7 | 4 3 | 3 4 | 1 3 |
O | 4 3 | 1 3 | 3 1 | 1 0 |
S | 1 3 | 1 0 | 0 1 | O |
O | 1 0 | S | S | O |
S | O | S | S | O |
首先 \(a/b>1\) , 先手有很多选择,在之后 \(a/b=1\) 都只有一种选择,死磕下去,胜负取决于前边的选择减几倍
在完美的操作下,先手一定会赢。
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
bool dfs(ll a,ll b,int who)
{
if(!b) return false;
if(a/b==1) return !dfs(b,a-b,who^1);
if(a/b>1) return true;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
ll k;
scanf("%lld",&k);
ll a,b;
while(k--)
{
scanf("%lld%lld",&a,&b);
if(a<b) swap(a,b);
if(dfs(a,b,1)) printf("Stan wins\n");
else
printf("Ollie wins\n");
}
}
旅行商
题目描述
对国家的城市 1~n 编号,互质的编号城市修双向道路,其余不修,一名旅行商想知道从首都(编号1)经过每个城市恰好一次回到首都的路径数
输入格式
一个正整数n
输出格式
一个数表示答案 ,对 1000000007取模
输入样例
4
输出样例
2
爆搜拿30!!性价比最高
爆搜
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
long long sum;
const int maxn=30,maxm=100;
bool vis[50];
struct Edge{
int next,to;
}edge[maxm];
int head[maxn],num_edge;
void add_edge(int from,int to){
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
int gcd(int a,int b){
return b? gcd(b,a%b):a;
}
void dfs(int u ,int city)
{
vis[u]=1;
if(city==n){//临界条件
sum++;
sum%=1000000007;
return;//回溯
}
for(int i=head[u];i;i=edge[i].next)
if(!vis[edge[i].to])
{
dfs(edge[i].to,city+1);//下搜
vis[edge[i].to]=0; //恢复原状
}
}
int main()
{
freopen("merchant.in","r",stdin);
freopen("merchant.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
for(int j=i+1;j<=n;j++)
if(gcd(i,j)==1)
{
add_edge(i,j);
add_edge(j,i);
}
//cout<<num_edge;//检验边数
dfs(1,1);
printf("%lld",sum);
}
等我在看懂标程后在更新吧QWQ