题目一:传球游戏
ball.c/cpp/pas
Time Limit:1s Memory Limit:256M
题目描述:
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到了小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并且假设小蛮为1号,球传了3次回到小蛮手里的方式有1->2->3->1和1->3->2->1,共2种。
输入格式
输入文件ball.in共一行,有两个用空格隔开的整数n,m
40%的数据满足:3<=n<=30,1<=m<=20
100%的数据满足:3<=n<=30,1<=m<=30
输出格式
输出文件ball.out共一行,有一个整数,表示符合题意的方法数。
样列输入
3 3
样列输出
2
一道简单的dp
设dp[i][j]是第i步到达第j个位置的方法数,那么dp[i][j] = dp[i - 1][j + 1] + dp[i - 1][j - 1]
由于是环形,所以注意边界就好了
#include
#include
using namespace std;
int n,m;
int dp[50][50];
int main()
{
freopen("ball.in", "r", stdin);
freopen("ball.out", "w", stdout);
scanf("%d%d", &n, &m);
dp[0][1] = 1;
for(int i = 1; i <= m; i++)
{
for(int j = 1; j <= n; j++)
{
dp[i][j] = dp[i - 1][(j + 1) > n ? 1 : j + 1] + dp[i - 1][(j - 1) <= 0 ? n : j - 1];
}
}
printf("%d",dp[m][1]);
return 0;
}
其实dfs也挺简单的
#include<iostream>
#include<cstdio>
using namespace std;
int ans = 0;
int n,m;
void dfs(int j, int p)
{
//printf("%d %d\n",j,p);
if(j == 0 && p == 1){
ans++;
//printf("++\n");
return ;
}
if(j == 0 && p != 1)return ;
if(p + 1 <= n)dfs(j - 1, p + 1);
if(p + 1 > n)dfs(j - 1, 1);
if(p - 1 > 0)dfs(j - 1, p - 1);
if(p - 1 < 1)dfs(j - 1, n);
}
int main()
{
freopen("ball.in", "r", stdin);
freopen("ball1.out", "w", stdout);
scanf("%d%d", &n ,&m);
dfs(m, 1);
printf("%d",ans);
return 0;
}
题目二: 纯洁的买卖
sale.c/cpp/pas
Time Limit:1s Memory Limit:256M
题目描述:
ALEJ并不是财迷,但是作为纯洁党的伟大领袖,不挣钱,纯洁的事业怎么能坚持下去!纯洁党现在已经有M(1<=M<=100000)元经费了。ALEJ有一个富II代朋友,叫做HSW,HSW特别喜欢高价收藏一些餐巾纸(毛泽东用过的)、袜子(恐龙穿过的)、马桶垫(还珠格格坐过的)、红领巾(毛新宇戴过的)等等,总之,没有他不买的。因此ALEJ想通过HSW这个大财主,去实现倒买倒卖拥有尽可能多的钱。
有N(1<=N<=100)件物品供他选择,
ALEJ每件物品的买入价为c[i](1<=c[i]<=100000)元,
HSW的收藏价为r[i](1<=r[i]<=100000)元。
每向HSW卖出一件物品i之后,还要向政府上交c[i]元的税。每种物品的数量都是无限的。
ALEJ想知道,通过一次买卖(种类、数量没有限制)后,纯洁党的经费能有多少。
输入格式
第一行两个整数,N,M。之后N行,每行两个整数:c[i],r[i]
输出格式
一个整数,表示一次买卖后手里最多的钱数。
样例输入
3 17
2 4
5 6
3 7
样例输出
22
思路:
完全裸得不能再裸的完全背包,稍微改下就行了。。。。。话说居然看错题,直接打纯的完全背包。。而且居然答案跟样例一样!!!!
设dp[i]为还剩i元经费的赚的最大值,c[i] 为第i件物品的买入,b[i]为交了税后的纯收入;
然后直接完全背包,最后输出dp[m] + m,
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int c[200],b[200];
int n, m;
long long dp[100000 + 10];
int main()
{
freopen("sale.in", "r", stdin);
freopen("sale.out", "w", stdout);
scanf("%d%d", &n ,&m);
memset(dp,0,sizeof(dp));
for(int i = 1; i <= n; i++){
scanf("%d%d", &c[i], &b[i]);
b[i] -= 2 * c[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = c[i]; j <= m; j++)
dp[j] = max(dp[j], dp[j - c[i]] + (long long)b[i]);
}
printf("%I64d",dp[m] + m);
return 0;
}
题目 三 : 多边形游戏
game.c/cpp/pas
Time Limit:1s Memory Limit:256M
题目描述
多边形游戏是一种在一个具有n个顶点的多边形上进行的游戏。每个顶点有个权值(整数)。如图1是一个n=4对应多边形,每个顶点上都有一个整数,每条边都有一个运算符+或者*,所有边按从1到n进行编号。
游戏都首先移除一条边,接下来可以进行如下操作:
选择一条边E和与之相关联的点V1和V2,用一个新的点替换它们,新点上的整数为V1,V2上的整数用E上的操作符运算后的结果。
没有边时游戏结束,游戏得分就是最后剩下的那个顶点上的整数。
对于图1中的多边形,如果游戏者首先去掉3,然后依次去掉1、4、2,最后得分将是0。
请你写一个程序,对于给定的多边形,计算出可能得到的最高分,并列出第一步移除哪些边可以得到这个最高分。
关于输入
输入第一行是一个正整数n(3<=n<=50),表示多边形的边数。
接下来一行是这个多边形的描述,包含n条边和n个顶点,加号边用t表示,乘号边用x表示,顶点用顶点上标的整数表示,输入按照边的编号从1到n的顺序给出。
关于输出
第一行输出可能得到的最高分。
第二行输出一些边的列表,只有第一步移除的边在这个列表中才可能得到最高分。每条边都用这个边上的编号表示,列表必须是升序的。
例子输入
4
t -7 t 4 x 2 x 5
例子输出
33
1 2
这道题好难好难好难。。。
由于随便去掉一条边,也就是将一条环断成一条链,有点像环形的石子归并。。。。那么我们就按那种思路去想,并且由于会出现负数,那么我们就不能单纯的只保留最大值,因为两个极小的负数相乘有可能会是个极大的数,所以我们应该保存最大值与最小值来判断。
设dpmax[i][j][k] 为去掉第i条边,从第j条边到第k条边的合并的最大值 dpmin[i][j][k]同理
那么我们可以枚举最后一次合并的断点来转移,注意是环形,至于去掉那些边可能得到最大值,一个循环扫一遍判断是否是最大值,若是,则加入答案,不是则跳过
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n;
char ope[50], tope[50];
int num[50],tnum[50];
int dpmax[50][50][50];
int dpmin[50][50][50];
int maxx;
int temp1,temp2,temp3,temp4;
int main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
memset(dpmax, -0x3f , sizeof(dpmax));
memset(dpmin, 0x3f , sizeof(dpmin));
int i, j, k, l;
int path[50];
scanf("%d ", &n);
for(i=0;i<2*n;i++)
{
if(i%2 == 0)
cin>>ope[i/2];
else
cin>>num[(i-1)/2];
}
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
tope[j] = ope[(i + 1 + j) %n ];
tnum[j]=num[(i + j) % n];
}
for(j = 0; j < n; j++)
{
dpmax[i][j][j] = tnum[j];
dpmin[i][j][j] = tnum[j];
}
for(j = 1; j <= n - 1; j++)
{
for(k = 0; k + j <= n - 1; k++)
{
for(l = k; l < k + j; l++)//枚举断点
{
if(tope[l] == 't')//找最大值与最小值
{
temp1 = dpmax[i][k][l] + dpmax[i][l + 1][k + j];
temp2 = dpmin[i][k][l] + dpmin[i][l + 1][k + j];
dpmax[i][k][k + j] = max(dpmax[i][k][k + j], max(temp1, temp2));
dpmin[i][k][k + j] = min(dpmin[i][k][k + j], min(temp1, temp2));
}
else
{
temp1 = dpmax[i][k][l] * dpmax[i][l + 1][k + j];
temp2 = dpmin[i][k][l] * dpmin[i][l + 1][k + j];
temp3 = dpmin[i][k][l] * dpmax[i][l + 1][k + j];
temp4 = dpmax[i][k][l] * dpmin[i][l + 1][k + j];
dpmax[i][k][k + j] = max(dpmax[i][k][k + j], max(temp1, max(temp2, max(temp3, temp4))));
dpmin[i][k][k + j] = min(dpmin[i][k][k + j], min(temp1, min(temp2, min(temp3, temp4))));
}
}
}
}
}
maxx = -0x3f3f3f3f;
j = 0;
for(i = 0;i < n; i++)
{
if(dpmax[i][0][n - 1] > maxx)
{
maxx = dpmax[i][0][n - 1];
j = 0;//因为当maxx更新后,以前的答案并不是最大值,所以我们应当重新记录
path[j++]=i + 1;//+1是因为从0开始
}
else if(dpmax[i][0][n - 1]==maxx)
path[j++] = i + 1;
}
printf("%d\n", maxx);
k = j;
for(j = 0;j <= k - 1; j++)
printf("%d ",path[j]);
return 0;
}