2014 Super Training #10 C Shadow --SPFA/随便搞

原题: FZU 2169 http://acm.fzu.edu.cn/problem.php?pid=2169

这题貌似有两种解法,DFS和SPFA,但是DFS怎么都RE,SPFA也要用邻接表表示边,用向量表示的话会TLE,而且用SPFA有一个异或,就是题目说要沿最短路走到都城,但是SPFA是走最短路去消灭叛军,然后再走回都城,我不知道怎么回事,不知道能不能有大神解释。因为这样的话,有多少叛军就能消灭多少叛军了,那就不要用什么算法 了,直接一个统计。于是试了一下,居然A了,瞬间变成大水题,我无法再评价这个题目了,就当消遣了。

SPFA法:依次从每个军队城市出发做一次SPFA,看到有能到的(肯定能到啊)叛军就将其消灭。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#define Mod 1000000007
using namespace std;
#define N 100007

struct Edge
{
    int v,next;
}G[2*N];
int head[N],tot;
int army[N],rebel[N];
int vis[N],dis[N];
int res;
int n,m;

void addedge(int u,int v)
{
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void SPFA(int s)
{
    int i;
    memset(vis,0,sizeof(vis));
    queue<int> que;
    while(!que.empty())
        que.pop();
    que.push(s);
    vis[s] = 1;
    dis[s] = 0;
    while(!que.empty())
    {
        int tmp = que.front();
        que.pop();
        vis[tmp] = 0;
        for(i=head[tmp];i!=-1;i=G[i].next)
        {
            int v = G[i].v;
            if(dis[v] > dis[tmp] + 1)
            {
                dis[v] = dis[tmp]+1;
                if(!vis[v])
                {
                    que.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
}

int main()
{
    int i,j,x;
    int u,v;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(army,0,sizeof(army));
        memset(rebel,0,sizeof(rebel));
        memset(head,-1,sizeof(head));
        tot = 0;
        int cnt = 0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&rebel[i]);
            if(rebel[i])
                cnt++;
        }
        for(i=1;i<=m;i++)
            scanf("%d",&army[i]);
        for(i=0;i<n-1;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        res = 0;
        for(i=1;i<=m;i++)
        {
            SPFA(army[i]);
            for(j=1;j<=n;j++)
            {
                if(dis[j] != Mod)
                {
                    if(rebel[j])
                    {
                        res += rebel[j];
                        rebel[j] = 0;
                        cnt--;
                    }
                }
            }
            if(cnt == 0)  //已经消灭完
                break;
        }
        printf("%d\n",res);
    }
    return 0;
}

DFS法(Runtime Error):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define Mod 1000000007
#define ll long long
using namespace std;
#define N 100007

struct Edge
{
    int v,next;
}G[2*N];
int head[N],tot;
int army[N],rebel[N];
ll sum;
int n,m;

void addedge(int u,int v)
{
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

int dfs(int u,int val,int fa)
{
    int soni = 0;
    if(army[u])  //找到军队
    {
        sum += val;
        soni++;
    }
    for(int i=head[u];i!=-1;i=G[i].next)
    {
        int v = G[i].v;
        if(v == fa)
            continue;
        soni += dfs(v,val+rebel[u],u);
    }
    if(soni > 1)
        sum -= (soni-1)*rebel[u];
    return soni;
}

int main()
{
    int i,j,x;
    int u,v;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(army,0,sizeof(army));
        memset(rebel,0,sizeof(rebel));
        memset(head,-1,sizeof(head));
        tot = 0;
        for(i=1;i<=n;i++)
            scanf("%d",&rebel[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%d",&x);
            army[x] = 1;
        }
        for(i=0;i<n-1;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        sum = 0;
        dfs(1,0,-1);
        printf("%lld\n",sum);
    }
    return 0;
}

直接统计:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
#define N 100007

int army[N],rebel[N];
int res;
int n,m;

int main()
{
    int i,j,x;
    int u,v;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(rebel,0,sizeof(rebel));
        int cnt = 0;
        res = 0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&rebel[i]);
            if(rebel[i])
                res += rebel[i];
        }
        for(i=1;i<=m;i++)
            scanf("%d",&army[i]);
        for(i=0;i<n-1;i++)
            scanf("%d%d",&u,&v);
        if(m == 0)
        {
            puts("0");
            continue;
        }
        printf("%d\n",res);
    }
    return 0;
}

2014 Super Training #10 C Shadow --SPFA/随便搞

时间: 2024-11-05 17:25:41

2014 Super Training #10 C Shadow --SPFA/随便搞的相关文章

2014 Super Training #10 G Nostop --矩阵快速幂

原题: FZU 2173 http://acm.fzu.edu.cn/problem.php?pid=2173 一开始看到这个题毫无头绪,根本没想到是矩阵快速幂,其实看见k那么大,就应该想到用快速幂什么的,况且n<=50,可以用矩阵来表示图. 1.为什么能用矩阵快速幂呢? 原理: 原始矩阵m[][]中,m[u][v]代表u到v的花费,求矩阵的k次幂后,此时m[u][v]代表,从u走向b经过v步的最少花费注意此时矩阵的相乘应该写成:m[a][b]=min(m[a][1]+m[1][b],...m[

2014 Super Training #10 D 花生的序列 --DP

原题: FZU 2170 http://acm.fzu.edu.cn/problem.php?pid=2170 这题确实是当时没读懂题目,连样例都没想通,所以没做了,所以还是感觉这样散漫的做不好,有些题目明明很简单,却因为没看懂而放弃了,甚至去玩了,这样达不到太大的效果. 解法: 定义: dp[i][j]:前i个字母中有j个是属于第一个序列的标号方案种数. 则当遇到'B'时,因为要满足WB依次间歇出现,所以前面属于第一个序列的个数应该为奇数,即j&1时转移.当属于第二个序列的个数为奇数时((i-

2014 Super Training #3 H Tmutarakan Exams --容斥原理

原题: URAL 1091  http://acm.timus.ru/problem.aspx?space=1&num=1091 题意:要求找出K个不同的数字使他们有一个大于1的公约数,且所有的数字都不能大于一个指定的数字S. 解法:可以考虑每个S内的素数,此素数和它的所有倍数构成一个集合,则可以在这些集合中任意去k个元素,C(n,k)即为这种情况下的方法种数,比如K = 3,S = 10, 则可以形成3个集合: {2,4,6,8,10} , {3,6,9}, {5,10} ,第一个集合C(5,

2014 Super Training #7 C Diablo III --背包问题(DP)

原题: ZOJ 3769 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3769 一个带有一些限制的背包问题. 假设在没有限制的情况下,那么定义:dp[i][j]表示在前 i 类物品中,总的Toughness为 j 的时候最大的伤害值. 取到第K类的第x个物品时(属性值为D,T),则有转移方程: dp[K][j+T] = max(dp[K][j+T],dp[K-1][j]+D) .其中j+T超过m时按m算就可以了. 但是

2014 Super Training #1 F Passage 概率DP

原题: HDU 3366   http://acm.hdu.edu.cn/showproblem.php?pid=3366 本来用贪心去做,怎么都WA,后来看网上原来是一个DP题. 首先按P/Q来做排序,即P越大,Q越小就越好,这样可以确保先选最优的路走. dp[i][j]表示已经到了第i条路(说明前i-1条都没成功的情况),还剩j块钱时能够走出去的概率. 则方程: dp[i][j] = way[i].P + way[i].Q*(dp[i+1][j-1]) + way[i].D*(dp[i+1]

2014 Super Training #8 B Consecutive Blocks --排序+贪心

当时不知道怎么下手,后来一看原来就是排个序然后乱搞就行了. 解法不想写了,可见:http://blog.csdn.net/u013368721/article/details/28071241 其实就是滑动窗口的思想. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algor

2014 Super Training #1 C Ice-sugar Gourd 模拟,扫描线

原题 HDU 3363 http://acm.hdu.edu.cn/showproblem.php?pid=3363 给你一个串,串中有H跟T两种字符,然后切任意刀,使得能把H跟T各自分为原来的一半. 解法: 把串想象成一个环,只要满足H跟T都为偶数个,那么就可以做一条过圆心的直线把H跟T平分掉,过直线,只要考虑平分H或者T中的一个就可以了,因为直线本来就把环平分,而此时平分了H或者T,那么剩下的那个也是平分掉的. 具体证明: http://hi.baidu.com/superlong/item

2014 Super Training #1 B Fix 状压DP

原题: HDU 3362 http://acm.hdu.edu.cn/showproblem.php?pid=3362 开始准备贪心搞,结果发现太难了,一直都没做出来.后来才知道要用状压DP. 题意:题目给出n(n <= 18)个点的二维坐标,并说明某些点是被固定了的,其余则没固定,要求添加一些边,使得还没被固定的点变成固定的,可见题目中的图形sample. 由于n很小,而且固定点的顺序没有限制,所以需要用状态压缩DP. 注意:1.当一个没固定的点和两个固定了的点连接后,该点就被(间接)固定了(

2014 Super Training #8 A Gears --并查集

题意: 有N个齿轮,三种操作1.操作L x y:把齿轮x,y链接,若x,y已经属于某个齿轮组中,则这两组也会合并.2.操作Q x y:询问x,y旋转方向是否相同(等价于齿轮x,y的相对距离的奇偶性).3.操作D x :拆下齿轮x,并且x所在的齿轮组不会断开4.操作S x : 查询齿轮x所在的齿轮组有多少齿轮.并查集,维护父节点的同时,dis记录一下每个节点到根节点的距离,并且用num记录一下以x为根节点的集合有多少个元素. 由于涉及到删除操作,删除的是根节点的话会导致信息丢失,所以在删除的时候直