2013腾讯编程马拉松复赛第二场一部分题解

2013腾讯编程马拉松复赛第二场部分题解

最近真是太水啦,就昨天的比赛来说,只过了一道。。。2013腾讯编程马拉松复赛第二场一部分题解,最后一道因为一个变量写反啦,一直WA到比赛结束,直接导致我没有看到1002这道大水题。。。唉,看来我真不是比赛型选手,今天把1005和1002了下,题解吧。


http://acm.hdu.edu.cn/showproblem.php?pid=4535

吉哥系列故事——礼尚往来

吉哥还是那个吉哥
  那个江湖人称“叽叽哥”的基哥
  
  每当节日来临,女友众多的叽叽哥总是能从全国各地的女友那里收到各种礼物。
  有礼物收到当然值得高兴,但回礼确是件麻烦的事!
  无论多麻烦,总不好意思收礼而不回礼,那也不是叽叽哥的风格。
  
  现在,即爱面子又抠门的叽叽哥想出了一个绝妙的好办法:他准备将各个女友送来的礼物合理分配,再回送不同女友,这样就不用再花钱买礼物了!
  
  假设叽叽哥的n个女友每人送他一个礼物(每个人送的礼物都不相同),现在他需要合理安排,再回送每个女友一份礼物,重点是,回送的礼物不能是这个女友之前送他的那个礼物,不然,叽叽哥可就摊上事了,摊上大事了......
  
  现在,叽叽哥想知道总共有多少种满足条件的回送礼物方案呢?


1001:有公式的,亏我还推了半天,数据量也不大,直接根据公式推就行,水题就不说了。。。


http://acm.hdu.edu.cn/showproblem.php?pid=4536


XCOM Enemy Unknown

XCOM-Enemy Unknown是一款很好玩很经典的策略游戏.
在游戏中,由于未知的敌人--外星人入侵,你团结了世界各大国家进行抵抗.
随着游戏进展,会有很多的外星人进攻事件.每次进攻外星人会选择3个国家攻击,作为联盟的指挥者,你要安排有限的联盟军去支援其中一个国家,抵抗进攻这个国家的外星人.
战斗胜利之后这个被支援的国家恐慌值就会-2点(恐慌值最少减为1),而其他两个未被支援的国家恐慌值就会+2点,同时和这两个国家在相同大洲的其他国家恐慌值也会+1点.
当一个国家的恐慌值超过5点,这个国家就会对联盟失去信心从而退出联盟.
现在给你外星人将会进攻的地点,问你最多能在不失去任何一个国家信任的情况下抵挡多少次外星人的进攻.
 
1002:啊啊啊,没看清数据量,没想到是DFS啊,我还蛋疼地想网络流啊。。。呜呜呜,今天写了个超级暴力的DFS过了,竟然才15ms。。。。直接枚举每一次的拯救目标就行,适当加些剪枝。代码如下:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define maxn 100010
#include <vector>
using namespace std;
int ask[110][3];
int aa[20];
int v[20];
int t[6][110];
int len[6];
int k;
int dfs(int num)
{
    if(num>k)
    return 0;
    int i,j,ans=0;
    for(i=0;i<3;i++)
    {
        int a=ask[num][i],b=ask[num][(i+1)%3],c=ask[num][(i+2)%3];
        if(aa[b]>3||aa[c]>3)
        continue;
        int tru=1,sum=len[v[b]];
        for(j=0;j<sum;j++)
        {
            if(aa[t[v[b]][j]]==5)
            {
                tru=0;break;
            }
        }
        if(!tru)
        continue;
        sum=len[v[c]];
        for(j=0;j<sum;j++)
        {
            if(aa[t[v[c]][j]]==5)
            {
                tru=0;break;
            }
        }
        if(!tru)
        continue;
        sum=len[v[c]];
        for(j=0;j<sum;j++)
        {
            aa[t[v[c]][j]]++;
        }
        sum=len[v[b]];
        for(j=0;j<sum;j++)
        {
            aa[t[v[b]][j]]++;
        }
        aa[b]++;
        aa[c]++;
        int tmp=aa[a];
        aa[a]=max(1,aa[a]-2);
        ans=max(ans,max(num,dfs(num+1)));
        sum=len[v[c]];
        for(j=0;j<sum;j++)
        {
            aa[t[v[c]][j]]--;
        }
        sum=len[v[b]];
        for(j=0;j<sum;j++)
        {
            aa[t[v[b]][j]]--;
        }
        aa[b]--;
        aa[c]--;
        aa[a]=tmp;
    }
    return ans;
}
int main()
{
    freopen("dd.txt","r",stdin);
    int ncase,time=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        int n,m,i;
        printf("Case #%d: ",++time);
        scanf("%d%d%d",&n,&m,&k);
        memset(len,0,sizeof(len));
        for(i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
            t[v[i]][len[v[i]]++]=i;
        }
        for(i=0;i<n;i++)
        scanf("%d",&aa[i]);
        for(i=1;i<=k;i++)
        scanf("%d%d%d",&ask[i][0],&ask[i][1],&ask[i][2]);
        int ans=dfs(1);
        printf("%d\n",ans);
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=4539


郑厂长系列故事——排兵布阵

郑厂长不是正厂长
  也不是副厂长
  他根本就不是厂长
  事实上
  他是带兵打仗的团长

  一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
  根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
  现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。


1005:这是昨天的啃死爹题,因为一个傻逼错误WA到最后。。。这道题一般人一看就知道是状态压缩DP了吧,但是我不是一般人啊,我是大傻X啊,就是只会往最大独立集去想啊。。。然后没想出来啊。。。

经典的状态压缩DP,我这DP水货遇到DP就怂。。。我们设dp[i][pre][now]表示第i行为now状态,第i-1行为pre状态时可以安排的最大士兵数量,这里的状态指的是每一行的士兵安排情况,我们把状态用二进制表示出来后,第i位为1表示在第i列放置一个士兵,为0表示不放,因为m<=10所以我们最多只要1024个状态就可以表示一行的每一个状态,事实上这1024个状态中有大部分是不合法的,(也就是有两个1其距离为2),所以我们可以处理出所有的合法状态,(我们下面所讨论的状态都是合法的),对于dp[i][pre][now]我要怎么计算呢

首先当然是这两个状态不能和所给的矩阵有冲突(也就是在不能人的地方放置了人),然后相邻两行不能有距离为2的1存在。满足上面的条件后,则dp[i][pre][now]=max(dp[i-1][ppre][pre]),其中ppre也要状态也要满足以上条件。我们最后求合法状态中的最大值即可,以上快速判断是否满足要求可以用位运算来判断,具体如何用自己思考,或者参考我的代码。

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <vector>
using namespace std;
int dp[200][200][200];
vector<int> a[11];
int check(int x,int len)
{
    int i;
    for(i=0;i<len-2;i++)
    {
        int tmp=(1<<i)+(1<<(i+2));
        if((tmp&x)==tmp)
        {
            return 0;
        }
    }
    return 1;
}
int cot[1100];
int getnum(int x)
{
    int sum=0;
    while(x)
    {
        if(x%2)
        sum++;
        x/=2;
    }
    return sum;
}
void init()
{
    int i,j,tmp;
    for(i=0;i<=1024;i++)
    cot[i]=getnum(i);
    for(i=1;i<=10;i++)
    {
        tmp=(1<<i)-1;
        a[i].push_back(0);
        for(j=1;j<=tmp;j++)
        {
            if(check(j,i))
            {
                a[i].push_back(j);
            }
        }
    }
}
int max(int a,int b)
{
    return a>b?a:b;
}
int num[110];
int main()
{
    init();
    int n,m,x;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        int i,j,tmp;
        for(i=1;i<=n;i++)
        {
            tmp=0;
            for(j=1;j<=m;j++)
            {
                scanf("%d",&x);
                tmp=tmp*2+x;
            }
            num[i]=tmp;
        }
        tmp=a[m].size();
        int ans=0;
        for(i=0;i<tmp;i++)
        {
            if((num[1]|a[m][i])==num[1])//判断是否满足棋盘
            {
                int now=a[m][i];
                dp[1][0][i]=cot[now];
                ans=max(ans,dp[1][0][i]);
            }
        }
        for(i=2;i<=n;i++)
        {
            for(j=0;j<tmp;j++)
            {
                if((num[i]|a[m][j])==num[i])//状态满足第i行
                {
                    int now=a[m][j];
                    int s,t;
                    for(s=0;s<tmp;s++)
                    {
                        int pre=a[m][s];
                        if((num[i-1]|pre)==num[i-1])
                        {
                            if(((now<<1)&pre)==0&&((now>>1)&pre)==0)
                           {
                               if(i==2)
                               dp[i][s][j]=dp[1][0][s]+cot[now];
                               else
                               {
                                   for(t=0;t<tmp;t++)
                                   {
                                       int ppre=a[m][t];
                                       if((num[i-2]|ppre)==num[i-2])
                                       {
                                           if((ppre&now)==0)
                                           {
                                               if(((ppre<<1)&pre)==0&&((ppre>>1)&pre)==0)
                                               dp[i][s][j]=max(dp[i][s][j],dp[i-1][t][s]+cot[now]);
                                           }
                                       }
                                   }
                               }
                               ans=max(ans,dp[i][s][j]);
                           }
                        }
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}