2017 UESTC Training for Dynamic Programming

2017 UESTC Training for Dynamic Programming

A    思维, 或 dp, 很有意思

方法1:

构造法:蛇形安排赛程表
算法复杂度:O(N^2)
将1-N排成两竖列,每一轮同一行的为对手
保持1的位置不变,其他位置按顺(逆)时方向依次旋转
1    6          1    2          1    3          1    4          1    5      
2    5          3    6          4    2          5    3          6    4      
3    4          4    5          5    6          6    2          2    3

1             N
2           N-1
3           N-2
.              .
.              .
.              .
N/2    N/2+1

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 200005;

int n, a[N];
int main()
{
    scanf("%d", &n);
    rep(i,2,n) a[i]=i;
    rep(i,n+1,2*n-1) a[i]=a[i-(n-1)];
    rep(i,2*n,3*n-2) a[i]=a[i-(n-1)];
    rep(i,n+1,2*n-1)
    {
        printf("1 %d ", a[i]);
        rep(ca,1,(n-2)/2)
        {
            printf("%d %d ", a[i-ca], a[i+ca]);
        }
        puts("");
    }

    return 0;
}

方法2:

DP做法
算法复杂度:O(N^3)
如果我们知道4支队伍的赛程表,就可以推出8支队伍的赛程表
1-2 3-4
1-3 2-4
1-4 2-3
第一部分:将8支队伍分成两组进行组内对抗
1-2 3-4        5-6 7-8
1-3 2-4        5-7 6-8
1-4 2-3        5-8 6-7
第二部分:两组队伍进行组间对抗
1-5 2-6 3-7 4-8
1-6 2-7 3-8 4-5
1-7 2-8 3-5 4-6
1-8 2-5 3-6 4-7
所以,我们如果能知道N(N为偶数)支球队的赛程表,就可以推出2*N支球队的赛程表
如果N为奇数,N支球队的赛程表和2*N支球队的赛程表该怎么推呢?
N为奇数的话,赛程表也得进行N轮,每轮会有一支球队轮空
比如说N=3(可假想N=4,多一个对手0,与0对阵算作轮空)
第一轮:1-2    3(轮空)
第二轮:1-3    2(轮空)
第三轮:2-3    1(轮空)
我们知道了三支球队的赛程表,六支球队的赛程表该这么转移
6支球队分成两组 先进行组内对抗
1-2     3(轮空)    4-5     6(轮空)
1-3     2(轮空)    4-6     5(轮空)
2-3     1(轮空)    5-6     4(轮空)
但是,我们不允许两支球队轮空,就得把分别在组内对抗按道理应轮空的两支球队对阵
1-2 4-5 3-6
1-3 4-6 2-5
2-3 5-6 1-4
再进行组间对抗
1-4 2-5 3-6    (这里的对阵在组内对抗进行过了,应去掉)
1-5 2-6 3-4
1-6 2-4 3-5

dp解法待补

D    01、完全 混合背包   可做板子

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 20005;

int t[N], a[N], b[N];

int n, volume, dp[N];
//memset(dp, t, sizeof(dp));  dp[0] = 0;  //恰好装满:t=128,最大:t=0
void ZeroonePack(int wi, int vi)
{
    for(int i=volume; i>=vi; --i)
        dp[i] = max(dp[i], dp[i-vi]+wi);
}
void CompletePack(int wi, int vi)
{
    for(int i=vi; i<=volume; ++i)
        dp[i] = max(dp[i], dp[i-vi]+wi);
}
void MultiplePack(int wi, int vi, int mi)
{
    if(mi*vi>volume) { CompletePack(wi, vi); return ; }
    for(int i=1; i<mi; mi-=i, i<<=1) ZeroonePack(wi*i, vi*i);
    ZeroonePack(wi*mi, vi*mi);
}

int main()
{
    scanf("%d %d", &n, &volume);
    rep(i,1,n) scanf("%d %d %d", &t[i], &a[i], &b[i]);
    mes(dp, 0);
    rep(i,1,n)
    {
        if(t[i]==1) CompletePack(a[i], b[i]);
        else ZeroonePack(a[i], b[i]);
        //MultiplePack(a[i], b[i], t[i]==1 ? 1e4+10 : 1);
    }
    printf("%d\n", dp[volume]);

    return 0;
}

F      递推dp,水题

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 1005;

int n, m, a[N][N], dp[N][N];
bool check(int x, int y)
{
    return (x>0 && y>0);
}
int main()
{
    scanf("%d %d", &n, &m);
    rep(i,1,n) rep(j,1,m)
        scanf("%d", &a[i][j]), dp[i][j]=-INF;
    dp[1][1]=a[1][1];
    int ans=-INF;
    rep(i,1,n) rep(j,1,m)
    {
        if(check(i-1, j) && dp[i-1][j]>0) dp[i][j]=max(dp[i][j], dp[i-1][j]+a[i][j]);
        if(check(i, j-1) && dp[i][j-1]>0) dp[i][j]=max(dp[i][j], dp[i][j-1]+a[i][j]);
        if(check(i-1, j-2) && dp[i-1][j-2]>0) dp[i][j]=max(dp[i][j], dp[i-1][j-2]+a[i][j]);
        if(check(i-2, j-1) && dp[i-2][j-1]>0) dp[i][j]=max(dp[i][j], dp[i-2][j-1]+a[i][j]);
        ans=max(dp[i][j], ans);
    }
    printf("%d\n", ans);

    return 0;
}

G     最长上升子序列,水,要打印路径,只会O(n^2)的。。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 200005;

int n, a[N], dp[N], path[N], ans[N];
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        rep(i,1,n) scanf("%d", &a[i]);
        rep(i,1,n)
        {
            dp[i]=1, path[i]=0;
            rep(j,1,i-1) if(a[j]<a[i])
            {
                if(dp[i] <= dp[j]+1) {
                    dp[i]=dp[j]+1;
                    path[i]=j;
                }
            }
        }
        int now=1, len=1;
        rep(i,1,n)
            if(len<dp[i]) len=dp[i], now=i;
            else if(len==dp[i] && a[now]>a[i]) now=i;
        printf("%d ", len);
        int k=0;
        while(now!=0) ans[++k]=a[now], now=path[now];
        per(i,k,1) printf("%d ",ans[i]);
        puts("");
    }

    return 0;
}

L    轮廓线动态规划(插头dp)

题意:给定一个n*m的矩阵,使用1*2的小长方形覆盖矩阵,要求完全覆盖的同时不出现重合,也不允许超出边界,问有多少种可能的覆盖方法,方案数对1e9+7取模 ( 2<=n<=1000, 3<=m<=5 )。

tags: 好难,看了题解。。

方法1 : 传统方法,以整行整列为状态。

x,y表示当前需要考虑放小长方形的位置,st1,st2分别是当前行和下一行的情况,dp[x][y][st1][st2]表示当前要考虑(x,y),当前行状态是st1,下一行状态是st2,可以有多少种合法的放置方法。 然后类似于记忆化dp。

状态转移:

if(y+1<=m && !(st1 & (1<<y)) && !(st1 & 1<<(y+1))) //横放,那么(x,y),(x,y+1)两个位置不能已经被覆盖
{
    res=(res+DP(x,y+2,(st1|(1<<(y)|(1<<(y+1)))),st2))%MOD; //如果状态转移合法,接下来就考虑(x,y+2),并且更新st1
}
if(x+1<=n && !(st1 & (1<<y))) //同上,竖放
{
    res=(res+DP(x,y+1,(st1|(1<<y)),(st2|(1<<y))))%MOD;
}

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 1005, mod = 1e9+7;

int n, m;
ll  dp[N][7][1<<6][1<<6];
inline ll  DP(int x, int y, int lin1, int lin2)
{
    if(x==n+1)  return 1;
    if(y==m+1)  return DP(x+1, 1, lin2, 0);
    if(dp[x][y][lin1][lin2]) return dp[x][y][lin1][lin2];
    if((lin1>>(y-1))&1) return DP(x, y+1, lin1, lin2);
    ll  res=0;
    if(y+1<=m && !((lin1>>(y-1))&1) && !((lin1>>(y))&1) )
    {
        res += DP(x, y+2, (lin1|(1<<(y-1))|(1<<(y))) , lin2);
        res %= mod;
    }
    if(x+1<=n && !((lin1>>(y-1))&1) && !((lin2>>(y-1))&1) )
    {
        res += DP(x, y+1, (lin1|(1<<(y-1))), (lin2|(1<<(y-1))) );
        res %= mod;
    }
    return dp[x][y][lin1][lin2]=res;
}
int main()
{
    scanf("%d %d", &n, &m);
    if(n*m&1) puts("0");
    else printf("%lld\n", (DP(1, 1, 0, 0)+mod)%mod);

    return 0;
}

方法2 : 插头dp,利用 “窄” 的特点,把参差不齐的 “轮廓线” 作为状态的一部分。

参考大白书 384页

伪代码(模板):

int dp[2][N], cur = 0;
所有d[0][j]初始化为1;
从小到大枚举每个要算的阶段
{
    cul ^= 1;
    所有dp[cul][j]初始化为0;  //只能在这里这样做,因为现在dp[cul]存着以前某阶段的值
    for 上个阶段的每个结点j
        for j的每个后继结点k
            d[cul][k] += d[1-cul][j];
}

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 1005, mod = 1e9+7;

int n, m, cur;
ll  dp[2][N];
void update(int a, int b)
{
    if(b&(1<<m)) dp[cur][b^(1<<m)] += dp[cur^1][a]%mod;
}
int main()
{
    scanf("%d %d", &n, &m);
    cur=0;
    rep(i,0,N-1) dp[cur][i]=1;
    rep(i,1,n) rep(j,1,m)  //枚举当前要算的阶段
    {
        cur^=1;
        mes(dp[cur], 0);
        for(int k=0; k<(1<<m); ++k)   //枚举上个阶段的状态
        {
            update(k, k<<1);    //不放
            if(i>1 && !(k&(1<<m-1))) update(k, (k<<1)|(1<<m)|1); //向上放
            if(j>1 && !(k&1)) update(k, (k<<1)|3); //向左放
        }
    }
    printf("%lld\n", (dp[cur][(1<<m)-1]+mod)%mod);

    return 0;
}

M    多重背包,和D题差不多

N    多位费用完全背包

题意:已知 n个参赛队员,对于第 i个队员,每写一行代码,就会留下 ai个bug。最后一题需要写 m行代码,请安排各个队员写的代码行数(显然要非负),使得整个代码的bug数不超过 b个。然后,在ACM玄学之神的保佑下,这份不超过b个bug的代码就能AC了。问你有多少种不同的安排方案可以写出一份AC代码,要求方案数对mod取模。

tags:

1、dp[i][j][k],已经考虑了前i个队员,写了j行代码,存在k个Bug的方案数,通过递推顺序,第一维可以直接省略。
2、if (k>=arr[i])   dp[j][k]=(dp[j][k]+dp[j-1][k-arr[i]])%mod;    // 实际含义是dp[i][j][k]=(dp[i-1][j][k]+dp[i][j-1][k-arr[i]]), 这里的递推顺序实际上是一个无穷背包的思想

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;++i)
#define per(i,b,a) for (int i=b;i>=a;--i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 505;

int n, m, b, mod, a[N];
ll   dp[N][N];
void CompletePack(int ai, int vi)
{
    rep(i,ai,b)  rep(j,vi,m)
    {
        dp[i][j] += dp[i-ai][j-vi];
        dp[i][j] %= mod;
    }
}
int main()
{
    scanf("%d %d %d %d", &n, &m, &b, &mod);
    rep(i,1,n)  scanf("%d", &a[i]);
    dp[0][0]=1;
    rep(i,1,n)
    {
        CompletePack(a[i], 1);
    }
    ll  ans=0;
    rep(i,0,b)
        ans = (ans+dp[i][m])%mod;
    printf("%lld\n", ans);

    return 0;
}

时间: 2024-12-14 13:33:27

2017 UESTC Training for Dynamic Programming的相关文章

2016 UESTC Training for Dynamic Programming

2016 UESTC Training for Dynamic Programming A - 柱爷与咸鱼神功 题意: 柱爷有n(<=5000)点心情去学m(<=5000)个招式,每个招式会得到一定的修炼值,但要消耗一定的心情,求最多的修炼值. 题解: 0.这是一个裸的背包问题,用每一个物品去更新每一种背包的状态. 1.状态定义:dp[i]表示用i点心情得到的最多修炼值. 2.状态转移:dp[i] = max{dp[i-v[j]]+w[j]} 代码: 1 2 3 4 5 6 7 8 9 10

UESTC_邱老师玩游戏 2015 UESTC Training for Dynamic Programming&lt;Problem G&gt;

G - 邱老师玩游戏 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 邱老师最近在玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中邱老师允许攻克M个城堡并获得里面的宝物. 但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡.你能帮邱老师算出要获得尽量多的宝物应该攻克哪M个城堡吗? In

UESTC_男神的约会 2015 UESTC Training for Dynamic Programming&lt;Problem J&gt;

J - 男神的约会 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 有一天男神约了学姐姐去看电影,电影院有一个活动,给你一个10*10的矩阵,每一个格子上都有一个0-9的整数,表示一共十种优惠券中的一种. 观众从左上角的格子开始走,走到右下角.每走到一个有着a号优惠券的格子,都必须要玩一个a分钟的游戏来领取这张优惠券. 每次只能向右或向下走.当走到右

UESTC_菲波拉契数制 2015 UESTC Training for Dynamic Programming&lt;Problem E&gt;

E - 菲波拉契数制 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 我们定义如下数列为菲波拉契数列: F(1)=1 F(2)=2 F(i)=F(i−1)+F(i−2)(i>=3) 给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和.比如13有三种表示法 13=13 13=5+8 13=2+3+8 现在给你一个数n,请输出把它表示成若干互

UESTC_酱神的旅行 2015 UESTC Training for Dynamic Programming&lt;Problem M&gt;

M - 酱神的旅行 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 酱神要去一棵树上旅行. 酱神制定了一个旅行计划,他要按顺序去m个树上的结点,a1,a2,a3,...,am. 酱神有一辆车,树上的每一条边既可以开车通过,也可以走过去,两种方法需要不同的时间.如果选择走路,酱神需要先把车停在结点上,在他下一次要开车的时候,必须先回到停车的结点取车.

UESTC_邱老师选妹子(二) 2015 UESTC Training for Dynamic Programming&lt;Problem I&gt;

I - 邱老师选妹子(二) Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 邱老师长得帅这是人尽皆知,于是追他的妹子就会很多.但是你知道,邱老师是一个很专一的人,所以他心里面只能有一个人.于是他决定从追他的众多妹子里挑选一个出来. 在第一轮的选拔中,剩余了一些妹子.酱神又给邱老师出主意了,因为最近酱神刚刚学习了最长上升子序列,所以这次,依然是把妹子们

UESTC_酱神赏花 2015 UESTC Training for Dynamic Programming&lt;Problem C&gt;

C - 酱神赏花 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others) Submit Status 酱神去杭州赏花. 花展在一条街道上举行,这条街道上有一共有n个节点,自左而右从1到n编号,1号和n号是左右两个端点,两个相邻端点之间的距离为1.本次花展一共要展出m朵花,在第ti时刻,有一朵颜值为bi的花将在第ai个节点展出,如果酱神在ti时刻处于第x个节点,那么他能获得的开心值为

UESTC_菲波拉契数制升级版 2015 UESTC Training for Dynamic Programming&lt;Problem L&gt;

L - 菲波拉契数制升级版 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 我们定义如下数列为菲波拉契数列: F(1)=1 F(2)=2 F(i)=F(i−1)+F(i−2)(i>=3) 给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和.比如13有三种表示法 13=13 13=5+8 13=2+3+8 现在给你一个数n,请输出把它表示成

UESTC_摩天轮 2015 UESTC Training for Dynamic Programming&lt;Problem K&gt;

K - 摩天轮 Time Limit: 10000/4000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others) Submit Status 一天,冬马被春希和雪菜拉着去一起去游乐园玩. 经过了各种过山车的洗礼后,三人决定去坐摩天轮休息下. 这是一个巨大的摩天轮,每一个车厢能坐任意多的人.现在,等着坐摩天轮的有n个人(包含他们3人),摩天轮还有m个车厢可以坐人.每个人都有自己肥胖程度,出于某些原因,胖子和瘦子坐在同一节车