“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 (部分题解)

“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛


比赛链接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= 接下来一行包含n个数,a1,a2,…,an(1<=ai<=10^5)

输出
对于每个样例,在一行输出答案。

样例输入
1
2
2 3

样例输出
1


题目分析:kss出的题orz,看了css的题解后来才发现其实就是裸的莫比乌斯反演,先预处理处1e5的莫比乌斯函数
因为mob[i]=1表示i是偶数个素因子的乘积,mob[i]=-1表示i是奇数个素因子的乘积,mob[i]=0表示其他
回到这道题上,首先对于总的集合我任意取出两个数的情况数为C(n,2)即  n * (n - 1) / 2,然后依次减去不满足的情况,即gcd为2的gcd为3的,这里就出问题了,因为如果gcd为6,那就被减了两次,所以要容斥一下,即拿总的情况-gcd只由一个素因子构成的情况+gcd只有两个素因子构成的情况。。。对于每个gcd值的集合我们可以用一个num数组记录。然后就可以看出莫比乌斯函数的强大了,容斥时对应的正负号其实就是mob数组,比如计算gcd为2的集合时,mob[2]==-1,因此减去 num[2] * (num[2] - 1) / 2,3的时候也是减,6的时候则是加,正好与mob函数的定义吻合


#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 
#include 
#include 
using namespace std;
map  mp;
map  :: iterator it;
int const MAX = 505;
int m, n, cnt;
int g[MAX][MAX], ind[MAX], tmp[MAX];
int re[MAX];
string s1, s2;

int TopoSort()
{
    bool flag = false;
    int len = 0;
    memcpy(tmp, ind, sizeof(ind));
    stack  s;
    for(int i = 0; i < cnt; i++)
        if(!tmp[i])
            s.push(i);
    while(!s.empty())
    {
        int pos = s.top();
        s.pop();
        re[pos] ++;
        len ++;
        for(int i = 0; i < cnt; i++)
            if(g[pos][i] && --tmp[i] == 0)
                s.push(i);
    }
    return len;
}

int main()
{
    while(scanf("%d", &m) != EOF)
    {
        mp.clear();
        cnt = 0;
        memset(g, 0, sizeof(g));
        memset(ind, 0, sizeof(ind));
        memset(re, 0, sizeof(re));
        while(m --)
        {
            cin >> s1 >> s2;
            it = mp.find(s1);
            if(it == mp.end())
                mp[s1] = cnt++;
            it = mp.find(s2);
            if(it == mp.end())
                mp[s2] = cnt++;
            g[mp[s2]][mp[s1]] = 1;
            ind[mp[s1]]++;
        }
        int ans = TopoSort();
        bool flag = false;
        scanf("%d", &n);
        while(n --)
        {
            cin >> s1;
            it = mp.find(s1);
            if(it == mp.end())  //如果这个算法没出现过,kss肯定就跪键盘了
            {
                flag = true;
                continue;
            }
            if(!re[mp[s1]])   //如果这个算法出现过,可是kss没学会。。可怜的kss
            {
                flag = true;
                continue;
            }
        }
        if(flag)
            printf("KSS have a dream!\n");
        else
            printf("Excelsior!\n");
    }
}



 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);
    }
}



你可能感兴趣的:(各类比赛,ACM)