2012 Multi-University Training Contest 8

官方解题报告:

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

题意:

给你一个n*n的矩阵,求另一个矩阵满足如下条件:

1.X12+X13+...X1n=1
2.X1n+X2n+...Xn-1n=1
3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n).

使得∑Cij*Xij(1<=i,j<=n) 最小,官方解题报告已经很完善这里不再罗嗦,只是强调一下在自己出现的错误:

1:这里分别得到由1,n出发的环视如果不存在返回的都是inf如果令inf = 0x7fffffff或者0x7f7f7f7f时,相加会查数据范围,这里要给出最大值为2^29

2:这里由1,n出发得到的环必须至少经过一个点,因为条件1,2应经限制了。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))



#define maxn 307

#define ll __int64

#define inf 536870912

#define MOD 100000007

using namespace std;



int mat[maxn][maxn],dis[maxn];

bool vt[maxn];

int n,m;



int SPFA(int s)

{

    int i;

    CL(vt,false);

    queue<int>q;

    while (!q.empty()) q.pop();

    for (i = 1; i <= n; ++i)

    {

        dis[i] = inf;

    }

    dis[s] = 0; vt[s] = true;

    q.push(s);

    int cir = inf;

    while (!q.empty())

    {

        int u = q.front(); q.pop();

        for (i = 1; i <= n; ++i)

        {

            if (dis[i] > dis[u] + mat[u][i])

            {

                dis[i] = dis[u] + mat[u][i];

                if (!vt[i])

                {

                    vt[i] = true;

                    q.push(i);

                }

            }

            if (i == u) continue;//这里控制由1,n出发得到的环必须至少经过一个点

            if (i == s && cir > dis[u] + mat[u][i])

            {

                cir = dis[u] + mat[u][i];

            }

        }

        vt[u] = false;

    }

    return cir;

}

int main()

{

   //freopen("din.txt","r",stdin);

   int i,j;

   while (~scanf("%d",&n))

   {

       for (i = 1; i <= n; ++i)

       for (j = 1; j <= n; ++j) scanf("%d",&mat[i][j]);



       int c2 = SPFA(1);//由1出发得到的环

       int c1 = dis[n];

       int c3 = SPFA(n);//由n出发得到的环

       printf("%d\n",min(c2 + c3,c1));

   }

   return 0;

}

 

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

题意:

有一个n曾的楼,从第一层往上走,每一层都有m块,可以从对应的本层的第y块跳到上一层的第y块,要求限制每一层只能往左右走,而且走的最大长度为T,问到达第n才层后的最大得分(ps:这里每一块都对应着一个得分,给出n*m举证表示得分);

思路:

这里简单的dp O(n*m*m)肯定会超时,所以这里要用单调队列来优化,这道题目和上次比赛的1003一样都是用单调队列来优化的。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))





#define N 107

#define M 10007

#define inf 0x7f7f7f7f

using namespace std;



int dp[N][M],sum[N][M],map[N][M];

int q[M*2];

int n,m,X,T;





int main()

{

    //freopen("din.txt","r",stdin);

   int i,j;

   while (~scanf("%d%d%d%d",&n,&m,&X,&T))

   {

       CL(sum,0);

       for (i = 1; i <= n; ++i)

       {

           for (j = 1; j <= m; ++j)

           {

               scanf("%d",&map[i][j]);

               sum[i][j] = sum[i][j - 1] + map[i][j];

               dp[i][j] = -inf;

           }

       }



       dp[1][X] = map[1][X];

       for (j = X + 1; j <= m && j - X <= T; ++j)

       dp[1][j] = dp[1][j - 1] + map[1][j];

       for (j = X - 1; j >= 1 && X - j <= T; --j)

       dp[1][j] = dp[1][j + 1] + map[1][j];



        int front,tail;

       for (i = 2; i <= n; ++i)

       {

           front = 0,tail = -1;

           //左扫

           for (j = 1; j <= m; ++j)

           {

               while (tail >= front && j - q[front] > T) front++;//保持对头满足小于T

               if(dp[i - 1][j] != -inf)//因为得分由负值,所以要限制一下

               {

                   int tmp = dp[i - 1][j] + map[i][j];

                   while (tail >= front && tmp > dp[i - 1][q[tail]] + sum[i][j] - sum[i][q[tail] - 1]) tail--;

                   q[++tail] = j;

               }





               if (tail >= front) dp[i][j] = max(dp[i][j],dp[i - 1][q[front]] + sum[i][j] - sum[i][q[front] - 1]);

           }

           //右扫

           front = 0,tail = -1;

           for (j =  m; j >= 1; --j)

           {

               while (tail >= front && q[front] - j > T) front++;

               if(dp[i - 1][j] != -inf)

               {

                   int tmp = dp[i - 1][j] + map[i][j];

                   while (tail >= front && tmp > dp[i - 1][q[tail]] + sum[i][q[tail]] - sum[i][j - 1]) tail--;

                   q[++tail] = j;

               }



               if (tail >= front) dp[i][j] = max(dp[i][j],dp[i - 1][q[front]] + sum[i][q[front]] - sum[i][j - 1]);

           }

       }



       int MAX = -inf;

       for (i = 1; i <= m; ++i)

       {

           MAX = max(MAX,dp[n][i]);

       }

       printf("%d\n",MAX);



   }

    return 0;

}

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

题意:

定义:U(A) 为序列A的最长上升子序列,D(A)为序列A的最长下降子序列,H(A) = max{U(A), D(A)}  求1-n的全排列里面所有序列的的H(A)的最小值。

思路:参考官方解题报告

举例10 -- 16 他们的sqrt()都为4 而10到12 都是分出三段,13到16 都是分出四段,我们按长度为4= sqrt(n)来分的,他的最长下降子序列已经确定为4,而最长上升子序列不能超过m所以对于分出三段的来说只要他的最前边的一段取出1个或者2个即可,又因为我们呢要保证字典序最小,所以我们把1放在最前边剩余逆序即可,而对于分出四段的我们必须从前边取出一个故必须降序。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))



#define maxn 100007

#define ll __int64

#define inf 0x7f7f7f7f

#define MOD 100000007

using namespace std;



int ans[maxn];

int main()

{

    //freopen("din.txt","r",stdin);

    int t,i;

    int n,m,len;

    scanf("%d",&t);

    while (t--)

    {

        len = 0; int s,e;

        scanf("%d",&n);

        m = ceil(sqrt(1.0*n));

        s = 1;

        if (m*(m - 1) >= n)//判断是否分出m-1段

        {

            ans[len++] = 1;//保证字典序最小

            s++;

        }

        //其余逆序

        e = (n - 1)%m + 1;

        for (i = e; i >= s; --i) ans[len++] = i;

        s = e + 1;

        int tmp = m - 1;



        while (s + tmp <= n)

        {

            e = s + tmp;

            for (i = e; i >= s; --i) ans[len++] = i;

            s = e + 1;

        }

        for (i = 0; i < len - 1; ++i) printf("%d ",ans[i]);

         printf("%d\n",ans[i]);

    }

    return 0;

}

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

题意:

序列{X1, X2, ... , Xn}由 Xk = (A * k + B) % mod产生,在序列{X1, X2, ... , Xn}中选一个子序列 {Y1, Y2, ... , Ym}是盖子序列满足:

 Yi + Yj <= L (1 ≤ i < j ≤ m), and every Yi <= L (1 ≤ i ≤ m ) 求最长的m

这道题目最坑爹了,卡数据类型,思路参考官方解题报告:

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))



#define maxn 107

#define ll __int64

#define inf 0x7fffffff

#define MOD 1000007

#define N 1000007

#define M 30000

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

using namespace std;



int main()

{

    //freopen("din.txt","r",stdin);

    int i;

    ll A,B,L,n,mod;

   // printf("%d\n",inf);

    while (scanf("%I64d%I64d%I64d%I64d%I64d",&n,&L,&A,&B,&mod) != EOF)

    {

        int mid = L/2;

        int cnt = 0;

        ll MIN = 1; MIN <<= 62;

        ll MAX = 0;

        for (i = 1; i <= n; ++i)

        {

            ll tmp = (A*i + B)%mod;

            if (tmp <= mid)

            {

                cnt++;

                if (MAX < tmp) MAX = tmp;

            }

            else

            {

                if (MIN > tmp) MIN = tmp;

            }

        }



        if (MIN + MAX <= L) cnt++;

        printf("%d\n",cnt);

    }

    return 0;



}

 

 

你可能感兴趣的:(test)