“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛
比赛链接http://acm.njupt.edu.cn/acmhome/contest.do?&method=contestDetail&contestId=215 FFF
FFF
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 130 测试通过 : 9
题目描述
FFF团,一个异端审判组织,团员活跃在各个角落,每当烧烤节来临的时候,我们都能听到他们传播的那熟悉的旋律:
烧啊~烧啊~烧啊烧啊烧~ (请自行脑补《当》)
FFF团成员自带这样一个属性:凭空变出火把与汽油,两者配合起来才能让FFF之火duang的一下烧起来,但是不同的火把与不同的汽油配合产生的火焰是不同的,现在有n种火把与n种汽油,已知每一种火把与每一种汽油配合时产生的火焰的旺盛程度,现在求怎样使得火把与汽油一一配对,产生最旺盛的火焰。
输入
第一行为一个整数T,表示有T组数据
每组数据第一行为一个正整数n(2≤n≤30)
第二行开始一共有n行,每行为n个正整数,第i行第j个数表示第i种火把与第j种汽油配合的火焰的旺盛程度。(0
输出
每组数据输出一个整数,表示最大的火焰旺盛程度
样例输入
2
3
5 2 6
6 7 9
7 4 1
4
8 5 2 8
5 8 2 1
9 6 3 7
7 5 8 1
样例输出
20
33
题目来源
kojimai
题目分析:开始没多久就被秒掉的题。。。果然是个裸模板,二分图带权最大匹配,套一个km的板子就行了,km算法就不在这说了
#include
#include
int const MAX = 50;
int const INF = 0x3fffffff;
int n, nx, ny;
int link[MAX], lx[MAX], ly[MAX], slack[MAX];
int visx[MAX], visy[MAX], w[MAX][MAX];
int DFS(int x)
{
visx[x] = 1;
for(int y = 1; y <= ny; y++)
{
if(visy[y])
continue;
int t = lx[x] + ly[y] - w[x][y];
if(t == 0)
{
visy[y] = 1;
if(link[y] == -1 || DFS(link[y]))
{
link[y] = x;
return 1;
}
}
else if(slack[y] > t)
slack[y] = t;
}
return 0;
}
int KM()
{
memset(link, -1, sizeof(link));
memset(ly, 0, sizeof(ly));
for(int i = 1; i <= nx; i++)
{
lx[i] = -INF;
for(int j = 1; j <= ny; j++)
if(w[i][j] > lx[i])
lx[i] = w[i][j];
}
for(int x = 1; x <= nx; x ++)
{
for(int i = 1; i <= ny; i ++)
slack[i] = INF;
while(true)
{
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(DFS(x))
break;
int d = INF;
for(int i = 1; i <= ny; i++)
if(!visy[i] && d > slack[i])
d = slack[i];
for(int i = 1; i <= nx; i++)
if(visx[i])
lx[i] -= d;
for(int i = 1; i <= ny; i++)
if(visy[i])
ly[i] += d;
else
slack[i] -= d;
}
}
int res = 0;
for(int i = 1; i <= ny; i++)
if(link[i] > -1)
res += w[link[i]][i];
return res;
}
int main ()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
nx = ny = n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &w[i][j]);
int ans = KM();
printf("%d\n", ans);
}
}
pdf的旅游
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 100 测试通过 : 21
题目描述
a协有一位pdf是一位旅(作)游(死)爱好者,曾经在出去比赛之后拐带学弟翘课一周,四处旅游。
最近pdf又突发奇想,想再出去旅游一次。出去旅游之前当然要好好计划一番。Pdf给自己想去的地方并给它们编好了号(起点为1)。为了不跟自己过不去,pdf提前找出哪些地点存在交通方便的路径,而且只会走这些路径。
为了旅途乐趣的最大化,pdf希望每个地点都到访过的同时,又保证来去都不会走重复的路,而且最终还要回到起点。问想要达到上述要求,pdf应该怎么样安排地点的访问顺序。
输入
第一行一个整数t,表示数据组数。
每组数据的第一行两个整数n,m分别表示地点数和路径数。(2≤n≤10,n≤m≤n(n-1)/2)
接下来的m行每行两个整数x,y 表示x,y之间交通方便。(保证两点之间只会有一条路)(1≤x,y≤n)
输出
每组数据输出一行,从起点开始的游览顺序。以空格分隔开。如果存在多解,则输出字典序最小的路径。(除起点外每个点只能访问一次)
样例输入
2
4 4
1 3
2 3
2 4
1 4
5 7
1 4
1 5
4 2
5 3
3 2
5 4
2 1
样例输出
1 3 2 4 1
1 2 3 5 4 1
题目来源
kojimai
题目分析:第一眼看以为是欧拉回路,拍完发现并不是,其实就是一道搜索题,其实因为n只有10,暴力就能过了,枚举全排列,模拟一下
#include
#include
#include
using namespace std;
int flag[20][20];
int p[11];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
memset(flag, 0, sizeof(flag));
int n, m;
scanf("%d %d", &n, &m);
for(int i = 0; i < m; i++)
{
int a, b;
scanf("%d %d", &a, &b);
flag[a][b] = 1;
flag[b][a] = 1;
}
for(int i = 0; i < n; i++)
p[i] = i + 1;
p[n] = 1;
do
{
bool f = true, f2 = false;
for(int i = 0; i < n; i++)
{
if(i == n - 1 && !flag[p[i]][1])
{
f = false;
break;
}
if(i != n - 1 && !flag[p[i]][p[i + 1]] && !flag[p[i + 1]][p[i]])
{
f = false;
break;
}
}
if(f)
break;
}while(next_permutation(p, p + n));
for(int i = 0; i < n; i++)
printf("%d ", p[i]);
printf("1\n");
}
}
特技的幂
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 481 测试通过 : 103
题目描述
幂运算是常见的数学运算之一,其原理是用同一个数相乘多次,但是有的时候当幂指数特别大的时候,这样的运算就太浪费时间。请大家学会在幂中加特技,让幂运算的效率提高到可以接受的程度。
输入
第一个行一个整数T,表示有T组数据
每组数据,输入x,y 求x的y次幂 (2≤ x ,y≤10^9)
输出
每组数据输出一个整数,表示幂运算对1000000007取模后的结果
样例输入
2
2 4
2 100000000
样例输出
16
494499948
题目来源
kojimai
题目分析:这。。。
#include
#define ll long long
int const MOD = 1e9 + 7;
ll quick_pow(ll a, ll b,ll m)
{
ll d = 1, t = a;
while(b > 0)
{
if(b % 2)
d = (d * t)%m;
b /= 2;
t = (t * t) % m;
}
return d;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
ll a, b;
scanf("%I64d %I64d", &a, &b);
printf("%I64d\n", quick_pow(a, b, MOD));
}
}
天神小学
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 145 测试通过 : 50
题目描述
《corpse party:blood drive》中有这么一段,班长筱崎亚由美拿到六鬼门的晶石,导致了涅?的暴走,天小的崩溃,靠着幸子的力量才逃出了天小。(剧情什么的不重要)
现在我们假设没有幸子,班长需要靠自己的力量逃出天神小学。可以把天神小学看作是一个二维的迷宫,每一秒都只能从当前位置走到上下左右四个相邻的格子里,因为天小一直在崩溃,所以有很多点是无法行走的。问班长能不能在天小完全崩溃,即t秒内逃出天神小学。
输入
第一行一个整数T,表示数据组数
每组数据第一行输入3个整数n,m,t分别表示迷宫的行数,列数,以及距离天小崩溃剩余的时间。(3≤n,m≤20,t≤100)
接下来输入n行,每行有一个长度为m的字符串。
其中字符’.’表示可以通行
字符’*’表示无法通行
字符’O’表示出口
字符’X’表示班长的起始位置
输出
若能逃离 输出 "happy end"
否则输出 "bad end"
样例输入
2
5 5 13
.....
.***.
.*X*O
.*.*.
...*.
5 5 14
.....
.***.
.*X*O
.*.*.
...*.
样例输出
bad end
happy end
题目来源
kojimai
题目分析:应该找不到比这个更裸的BFS了。。。
#include
#include
#include
using namespace std;
int const MAX = 25;
char map[MAX][MAX];
int vis[MAX][MAX];
int dirx[4] = {1,-1,0,0};
int diry[4] = {0,0,-1,1};
int n, m, t;
int sx, sy, ex, ey;
struct Point
{
int x, y;
int step;
};
int BFS()
{
memset(vis,0,sizeof(vis));
queue q;
Point node, t;
node.x = sx;
node.y = sy;
node.step = 0;
vis[sx][sy] = 1;
q.push(node);
while(!q.empty())
{
node = q.front();
q.pop();
for(int i = 0; i < 4; i++)
{
t = node;
t.x += dirx[i];
t.y += diry[i];
t.step++;
if(t.x == ex && t.y == ey)
return t.step;
if(t.x < 0 || t.x >= n || t.y < 0 || t.y >= m || map[t.x][t.y] == '*' || vis[t.x][t.y])
continue;
else
{
vis[t.x][t.y] = 1;
q.push(t);
}
}
}
return 10000;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d %d %d",&n, &m, &t);
for(int i = 0; i < n; i++)
{
scanf("%s", map[i]);
for(int j = 0; j < m; j++)
{
if(map[i][j] == 'X')
{
sx = i;
sy = j;
}
if(map[i][j] == 'O')
{
ex = i;
ey = j;
}
}
}
int ans = BFS();
if(ans <= t)
printf("happy end\n");
else
printf("bad end\n");
}
}
Dreaming
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 88 测试通过 : 8
题目描述
我们定义一个数good当且仅当它只由a和b构成,且数位和sum各数位也仅由a和b构成。举个栗子:若a=1,b=2,那么13不是good,11是(都由a=1构成,数位和sum=2由b=2构成)。那么窝们定义一个数的长度为n,那么有多少个数是good呢?所求答案对10^9+7取模。
输入
多组样例。
每行包含三个数a,b,n(1<=a,b<=9,1<=n<=10^6)
输出
每组数据输出一个整数。
样例输入
1 3 3
样例输出
1
题目分析:首先我们求各种可能的和,然后分解判断是不是满足条件,接着求组合数就行了比如长度为10,3个a+7个b满足,则这种情况就有C(10,3)个,这题时间卡的太紧了,组合数要用阶乘+模逆元算,阶乘得预处理,此代码目前是这题得最快速度
#include
#define ll long long
int const MAX = 1e6 + 2;
int const MOD = 1e9 + 7;
ll fac[MAX];
int n, a, b;
void pre()
{
fac[0] = fac[1] = 1;
for(int i = 2; i <= MAX; i++)
fac[i] = (fac[i - 1] * i) % MOD;
}
bool judge(int x)
{
while(x)
{
if(x % 10 != a && x % 10 != b)
return false;
x /= 10;
}
return true;
}
ll inv(ll x)
{
ll res = 1, y = MOD - 2;
while(y)
{
if(y & 1)
res = (res * x) % MOD;
x = (x * x) % MOD;
y >>= 1;
}
return res;
}
ll C(int n, int m)
{
return fac[n] * inv(fac[m]) % MOD * inv(fac[n - m]) % MOD;
}
int main()
{
pre();
while(scanf("%d %d %d", &a, &b, &n) != EOF)
{
int ans = 0;
for(int i = 0; i <= n; i++)
{
int sum = a * i + b * (n - i);
if((sum % 10 != a) && (sum % 10 != b))
continue;
else if(judge(sum / 10))
ans = (ans % MOD + C(n, i) % MOD) % MOD;
}
printf("%d\n", ans);
}
}
自动售货机
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 56 测试通过 : 12
题目描述
教学楼有一台奇怪的自动售货机,它只售卖一种饮料,单价5元,并且只收5元、10元面值的货币,但是同学们都很喜欢喝。这个售货机里没有多余的找零,也就是说如果一个持有10元的同学第一个购买,则他不能获得5元找零,但是如果在他之前有一个持有5元的同学买了这种饮料,则他可以获得5元找零。
假设售货机的货源无限,每人只买一罐,现在有N个持有5元的同学和M个持有10元的同学想要购买,问一共有多少种排队方法可以让每个持有10元的同学都获得找零。(这里的排队方法以某一位置上人持的钱数来分,即只要同一位置上的同学所持钱的数目相同,就算同一种排队方法)
输入
多组测试数据
每组包含两个整数N,M(1<=M<=N<=1000),分别表示持有5元和10元的同学个数。
输出
输出一个整数,表示排队方法总数。由于结果可能很大,所以结果需要模1000000007。
样例输入
1 1
2 1
3 1
样例输出
1
2
3
题目来源
hjp
题目分析:这题其实很简单,开始想的太复杂了,开始当作卡特兰数 (Catalan数)做其实就是(C(n+m, m)%mod - C(n+m, m-1)%mod)%mod,数组递推组合数T了 ,用java写大数结果noj上mle,连乘组合数取余写跪了,下面说这题应该怎么做。
其实就是一个dp,dp[i][j]表示有i个人有5元,j个人有10元的排队方案数,我们可以发现:
dp[i][0] = 1,因为大家都只有5元,怎么排都是一种方案
i < j时dp[i][j] = 0,因为有5元的人比有10元的少,必然会出现找不开的情况,那么此时方案数就是0
不是以上两种情况时:dp[i][j]的方案数由dp[i - 1][j] + dp[i][j - 1]递推得到,考虑第m+n个人的状态
1.第m+n个人100,m+n-1里有m个50,n-1个100则dp[m][n-1]
2.第m+n个人50,m+n-1里有m-1个50,n个100则dp[m-1][n]
#include
#include
int dp[1005][1005];
int const MOD = 1e9 + 7;
int main()
{
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= 1000; i++)
dp[i][0] = 1;
for(int i = 1; i <= 1000; i++)
for(int j = 1; j <= i; j++)
dp[i][j] = ((dp[i][j - 1] % MOD) + (dp[i - 1][j] % MOD)) % MOD;
int n, m;
while(scanf("%d %d", &m, &n) != EOF)
printf("%d\n", dp[m][n] % MOD);
}
Prime
时间限制(普通/Java):1000MS/3000MS 运行内存限制:65536KByte
总提交:267 测试通过:11
比赛描述
给定n个数,求两两互斥的对数。互斥是指两个数的最大公约数是1
输入
第一行为样例数T(T<=5)
对每个样例,第一行为一个整数n(2=
输出
对于每个样例,在一行输出答案。
样例输入
1
2
2 3
样例输出
1
#include
#include
#include
#define ll long long
using namespace std;
int const MAX = 1e5 + 5;
int p[MAX], cnt[MAX], num[MAX], mob[MAX];
bool prime[MAX];
int pnum, ma, n;
void Mobius() //求解莫比乌斯函数
{
pnum = 0;
mob[1] = 1;
memset(prime, true, sizeof(prime));
for(int i = 2; i < MAX; i++)
{
if(prime[i])
{
p[pnum ++] = i;
mob[i] = -1;
}
for(int j = 0; j < pnum && i * p[j] < MAX; j++)
{
prime[i * p[j]] = false;
if(i % p[j] == 0)
{
mob[i * p[j]] = 0;
break;
}
mob[i * p[j]] = -mob[i];
}
}
}
ll cal()
{
ll ans = (ll) n * (n - 1) / 2;
for(int i = 2; i <= ma ; i++)
{
num[i] = 0;
for(int j = i; j <= ma; j += i)
num[i] += cnt[j]; //得到gcd为i的集合的元素个数
}
for(int i = 2; i <= ma; i++)
ans += (ll) mob[i] * num[i] * (num[i] - 1) / 2;
return ans;
}
int main()
{
Mobius();
int T;
scanf("%d", &T);
while(T--)
{
memset(cnt, 0, sizeof(cnt));
ma = 0;
scanf("%d", &n);
for(int i = 0; i < n; i ++)
{
int tmp;
scanf("%d", &tmp);
cnt[tmp] ++;
ma = max(ma, tmp);
}
printf("%I64d\n", cal());
}
}
KSS的金牌梦1
时间限制(普通/Java) : 3000 MS/ 9000 MS 运行内存限制 : 65536 KByte
总提交 : 56 测试通过 : 7
题目描述
KSS是nupt集训队里公认的最具有金牌实力的选手,熟练掌握多种金牌算法,但是由于队友水平太菜和自身情绪不稳定,一直没能拿到金牌。KSS为了圆梦,想为自己制定一个训练计划,那么问题来了:
ACM中有许多算法之间是有单方面依赖关系的,比如:想学会A,就必须先学B,由于KSS很聪明,所以它可以学完A再学B;当然也存在两种或多种算法相互交融的情况,比如:想学会A,就必须先学B,想学会B,就必须先学A,这种情况KSS就不知从何下手了。
现在给出KSS打算学习的一些算法之间的依赖关系,KSS将尽自己最大的努力去学习这些算法。再给出比赛会出现的算法,如果KSS能学会超过70%的比赛算法,他就能圆梦,否则,他只能含恨退役。
输入
多组测试用例。
第一行一个整数N(0<=N<=250000)表示有N对算法间存在依赖关系,保证涉及的算法总数不超过500
接下来N行每行有两个字符串(以空格分割),表示前一个算法依赖后一个算法,第N+1行有一个整数M(0
输出
如果KSS可以圆梦,输出“Excelsior!”,否则,输出“KSS have a dream!”。(不用输出引号)
样例输入
4
Aho-Corasickautomaton KMP
Aho-Corasickautomaton trietree
Inclusion-ExclusionPrinciple Mobiusinversion
Mobiusinversion Inclusion-ExclusionPrinciple
5
KMP
trietree
Aho-Corasickautomaton
Splay
Suffixarray
样例输出
KSS have a dream!
提示
对于样例,KSS可以学会KMP、trietree、Aho-Corasickautomaton,但是并不能学会Inclusion-ExclusionPrinciple、Mobiusinversion,所以只能掌握60%的比赛算法
题目来源
hjp
题目分析:最伤心的一题。。。其实并不难可是全场就2人过还是在最后时候,所以并没有开它而是选择一直被数论坑着,赛后一下就补出来了。
这题就是裸的拓扑排序,出题人比较良心,没有卡map和cin的时间,不然字符串hash写就麻烦了,所以直接map存一下,建个图,拓扑排个序最后判断一下就行了,吐槽一下,我还是相信kss可以拿到金牌的
简单说下拓扑排序,就是用栈维护一个入读为0的点集,每次删点(出栈)然后修改图上各剩余点的入度,再将入度为0的点入栈,一直到栈为空,即不存在入度为0的点为止
#include
#include
#include
#include
Football
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 254 测试通过 : 64
题目描述
现在你是一名足球经理,你的队伍将参加“南邮杯”的比赛。然而你拥有预知未来的能力,你可以预见你的队伍接下来进行的n场比赛每场的进球数和失球数。每胜一场队伍可得3分,平一场可得1分,输一场得0分。然而“南邮杯”是有黑幕的,你通过砸钱现在可以买到m个进球,问现在如何安排这m个进球,可以使得队伍获得最大的积分,求出这个最大的积分。
输入
多样例输入。
第一行给出n(1<=n<=10)和m(0<=m<=20)分别代表你队伍进行的比赛数以及队伍可买的进球数。
接下来n行,每行分别有两个数x和y分别表示该场比赛在没有买进球的情况下你队伍的进球数和失球数。
输出
对于每个样例答案输出一行输出一个整数,表示通过买球的方式你的队伍可获得的最大积分。
样例输入
2 1
1 1
1 1
3 2
1 3
3 1
2 2
4 10
1 1
2 2
1 3
0 4
样例输出
4
6
12
题目分析:签到题,按差值(x - y)从大到小排序贪心,差值大于0的不用处理,小于等于0的模拟一下
#include
#include
#include
using namespace std;
int n, m;
struct Data
{
int x, y;
int val;
}d[100];
bool cmp(Data a, Data b)
{
return a.val > b.val;
}
int main()
{
while(scanf("%d %d", &n, &m) != EOF)
{
for(int i = 0; i < n; i++)
{
scanf("%d %d", &d[i].x, &d[i].y);
d[i].val = d[i].x - d[i].y;
}
sort(d, d + n, cmp);
for(int i = 0; i < n; i++)
{
if(d[i].val > 0)
continue;
else
{
while(d[i].val <= 0 && m > 0)
{
m --;
d[i].val ++;
}
}
if(m == 0)
break;
}
int ans = 0;
for(int i = 0; i < n; i++)
{
if(d[i].val > 0)
ans += 3;
if(d[i].val == 0)
ans += 1;
}
printf("%d\n", ans);
}
}
法师
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 172 测试通过 : 28
题目描述
说到法师,也许大家第一反应便是脆弱的身躯与强大的爆发能力。诚然如此,在《炉石传说》中,法师拥有着最高伤害的单体法术——炎爆术,同时还有同样高伤害的火球术。但法师并不仅限于此,如果说这两张火系法术代表着的是法师的爆发。那么冰系法术就代表了法师的控制,冰霜新星、冰锥术以及暴风雪和寒冰箭都能够使对手冻结。
每张卡牌能造成一定的伤害,同时也要花费一定的法力水晶。当法力水晶不够的时候,你便不能打出相应的卡牌。
为了简单起见,我们只考虑以下几张卡牌。
寒冰箭 :消耗2点法力水晶,对一个角色造成3点伤害,并使其冻结。
冰枪术:消耗1点法力水晶,使一个角色冻结,如果它已经被冻结,则改为对其造成4点伤害。
火球术:消耗4点法力水晶,造成6点伤害。
炎爆术:消耗10点法力水晶,造成10点伤害。
现在,告诉你现在拥有的法力水晶,以及手上拥有的这四种卡牌的数目(可能为0),问你能对敌方英雄造成多少点伤害。
输入
第一行为一个正整数T,表示有T组数据。
每组数据第一行有1个整数: n表示当前拥有的法力水晶个数0<=n<=10。
第二行为四个整数a,b,c,d分别表示拥有寒冰箭、冰枪术、火球术、炎爆术的数目。0<=a,b,c,d<=10.
输出
一个整数表示最大可能造成的伤害值。
样例输入
2
9
0 0 3 0
5
2 2 1 1
样例输出
12
11
作者:伟大的css
题目分析:css出题太良心了,看看数据范围,直接暴力搞一下,不过不知道有没有贪心或者dp解法
#include
#include
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int a, b, c, d, n;
scanf("%d", &n);
scanf("%d %d %d %d", &a, &b, &c, &d);
int ans = 0;
for(int i = 0; i <= a; i++)
{
for(int j = 0; j <= b; j++)
{
for(int k = 0; k <= c; k++)
{
for(int l = 0; l <= d; l++)
{
if(2 * i + 1 * j + 4 * k + 10 * l <= n)
{
if(i > 0) //用寒冰箭冰冻
ans = max(ans, 3 * i + 4 * j + 6 * k + 10 * l);
else
{
if(j >= 1) //用冰枪术冰冻
ans = max(ans, 4 * (j - 1) + 6 * k + 10 * l);
else
ans = max(ans, 6 * k + 10 * l);
}
}
}
}
}
}
printf("%d\n", ans);
}
}
送花
时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 115 测试通过 : 40
题目描述
萌妹纸一般都比较喜欢漂亮的鲜花。每逢各种节日,她们都想收到鲜花作为礼物。如果你是有妹纸滴人,经常不送妹纸花的话,结果可想而知了。
当然咯,妹纸都是通情达理的,不会因为某几次你木有送花,就发你好人卡了。王童鞋作为一个比较节俭(抠门)的人便知道这一道理,因此他想在妹纸不给他发好人卡的前提下,送尽量少的花。
为了简单起见,我们定义一个妹纸的幸福指数H(初始为0 )。如果某天幸福指数H小于0,那就。。。
如果某天妹纸收到了花,幸福指数H会增加ai,如果没收到,会下降bi。不同的日子送花对幸福指数的增加可能会有所不同,比如在2月14号送花就会比2月15号效果好~
即告诉你总天数n(1<=n<=365),每天收到花幸福指数的增加值ai(1<=ai<=10),没收到花幸福指数的降低值bi,求为了让妹纸的幸福指数H一直>=0,王童鞋至少要送妹纸多少朵花。
输入
第一行为一个正整数T,表示有T组数据。
每组数据第一行有1个整数: n表示总天数1<=n<=365。
第二行为n个整数ai表示第i天收到花幸福指数的增加值,1<=ai<=10。第三行为n个整数bi表示第i天没收到花幸福指数的下降值,1<=bi<=10。
输出
一个整数表示最少需要送多少朵花。
样例输入
2
1
3
4
5
5 2 10 1 1
1 1 1 5 5
样例输出
1
2
作者:伟大的kss
题目分析:据说这题数据n最大是10,贪心可搞,不过还是dp搞的。
dp[i][j]表示前i天妹子幸福值为j的送花数
#include
#include
#include
using namespace std;
int const MAX = 400;
int const INF = 0x3fffffff;
int dp[MAX][4000];
int a[MAX], b[MAX];
int main()
{
int T, n;
scanf("%d",&T);
while(T--)
{
int ans = INF;
int suma = 0;
scanf("%d",&n);
memset(dp, -1, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
suma += a[i];
}
for(int i = 1; i <= n; i++)
scanf("%d",&b[i]);
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= suma - a[i]; j++)
{
if(dp[i - 1][j] == -1) //前一天的状态不存在
continue;
if(dp[i][j + a[i]] == -1) //当天状态不存在则由前一天推出来
dp[i][j + a[i]] = dp[i - 1][j] + 1;
//当天状态存在则取最小,即判断前一天幸福值为j且当天送花得到的当天状态值
//是不是比原来的小,是就更新
else
dp[i][j + a[i]] = min(dp[i][j + a[i]], dp[i - 1][j] + 1);
}
//下面是不送花的情况,和上面类似
for(int j = b[i]; j <= suma; j++)
{
if(dp[i - 1][j] == -1)
continue;
if(dp[i][j - b[i]] == -1)
dp[i][j - b[i]] = dp[i - 1][j];
else
dp[i][j - b[i]] = min(dp[i][j - b[i]], dp[i - 1][j]);
}
}
//取前n天妹子幸福值大于等于0时送花的最小值
for(int j = 0; j <= suma; j++)
{
if(dp[n][j] == -1)
continue;
ans = min(ans, dp[n][j]);
}
printf("%d\n", ans);
}
}