无题。。。

7 篇文章 0 订阅
1 篇文章 0 订阅

@

背包dp

A. Cut Ribbon

链接:传送门
还是三种色带的话就是相当于三层dp嘛 所以这样子就okk了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout << #x << " == " << x << endl;
const ll int MAX_N = 1e5 + 10;
int dp[MAX_N] = {0};
int main()
{
    int n, b[4];
    scanf("%d %d %d %d", &n, &b[1], &b[2], &b[3]);
    for (int i = 1; i <= 3; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            if ((j - b[i] == 0) || (j >= b[i] && dp[j - b[i]]))
            {
                dp[j] = max(dp[j], dp[j - b[i]] + 1);
            }
        }
    }
    printf("%d\n", dp[n]);
    ///system("pause");
}

樱花(多重背包,二进制拆分)

链接:传送门

#include <bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << endl;
typedef pair<int, int> ppp;
const int INF = 0x3f3f3f3f; //要比hp大
const int MAX_N = 1e4 + 10;
struct node
{
    int t;
    int c;
    int p;
} tt[MAX_N];
#define ll long long
int dp[20005]= { 0 };
int cost[1000005] = { 0 };
int v[1000005] = { 0 };
int mod = 2;
int tot = 0;
int n;
inline void pre()
{
    for(int i=1;i<=n;i++)
    {
        int t = 1;
        while(tt[i].p)
        {
            tot++;
            cost[tot] = t * tt[i].t;
            v[tot] = t * tt[i].c;
            tt[i].p -= t;
            t *= 2;
            if(tt[i].p<t)
            {
                tot++;
                cost[tot] = tt[i].t *  tt[i].p;利用这个二进制拆分
                v[tot] = tt[i].c * tt[i].p;
                break;
            }
        }
    }
}
int main()
{
    int h1,m1,h2,m2;
    scanf("%d:%d %d:%d",&h1,&m1,&h2,&m2);
    int T=60*(h2-h1)+(m2-m1);

    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%d %d %d", &tt[i].t, &tt[i].c, &tt[i].p);
        if(tt[i].p==0)
        {
            tt[i].p=T/tt[i].t;
        }
    }
    pre();
    for(int i=1;i<=tot;i++)
    {
        for(int j=T;j>=cost[i];j--)
        {
            dp[j]=max(dp[j],dp[ j-cost[i] ] + v[i]);
        }
    }
    printf("%d\n",dp[T]);
}

普通dp

线性dp

P1233 木棍加工 (dilworth定理)

链接:

用贪心排序后,在n个数中,求不下降子序列最少个数。

根据dilworth定理,不下降子序列最小个数等于最大上升子序列的长度。
不上升子序列的最小个数大于最大下降子序列的长度。

#include <bits/stdc++.h>
using namespace std;
typedef long  long  ll;
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e5+5;
ll mod = 1e6+7;
struct node
{
    int l,w;
    bool operator<(node b)
    {
        if(l!=b.l)
            return l>b.l;
        return w>=b.w;
    }
}a[maxn];
int dp[maxn] = { 0 };
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a[i].l,&a[i].w);
    }
    sort(a+1,a+1+n);
    int ans = 0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i-1;j>=1;j--)
        {
            if(a[i].w>a[j].w)
            {
                dp[i]=max(dp[i],dp[j]+1);///常见dp板子
            }
        }
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans+1);
}

Dynasty Puzzles

链接:
今天又是读不懂题意的一天QwQ
题目大意:要是A字符串的首字母是B字符串的尾字母,B字符串的首字母是A字符串的尾字母两个字符串可以相连,求首尾最长字符串。所以我们可以dp写,dp[i][j]代表开头为字母i+‘a’,结尾为j+'a’的字符串的最长长度每次读入一个字符串
开头为beginn,结尾为endd更新每个dp[i][endd]还有dp[beginn][endd]就好,因为最后要得到的结果是dp[i][i]首尾字母相同的最长字符串,所以按着往后添加字符串就好,最终结果和往前添加相同,而且不会重复计算

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n'
const ll MAX_N =1e6+10;
int dp[30][30];
int n;
int main()
{
    int n;
    char a[30];
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",a);
        int len=strlen(a);
        int beginn=a[0]-'a';
        int endd=a[len-1]-'a';
        for(int i=0;i<26;i++)
        {
            if(dp[i][beginn])
            dp[i][endd]=max( dp[i][beginn]+len,dp[i][endd] );
        }
    }
    int ans=0;
    for(int i=0;i<26;i++)
    {
        ans=max( ans, dp[i][i] );
    }
    printf("%d\n",ans);
}

dp(cf里的不错好题)

CF933A A Twisty Movement

#include <bits/stdc++.h>
using namespace std;
typedef long  long  ll;
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn = 5e3+5;
ll mod = 1e6+7;
int dp[4] = { 0 };
///dp[1] 代表了 1,1.....
///dp[2] 代表了 1,1.....1,2....的情况
/// dp[3] 代表了 1,1.....1,(2...,1,...)翻转
/// dp[4] 代表了 1....,(2....,1....,)2....
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x == 1)
        {
            dp[1] = dp[1] + 1;
            dp[3] = max(dp[2],dp[3]) + 1;
        }
        else if(x==2)
        {
            dp[2] = max(dp[2],dp[1]) + 1;
            dp[4] = max(dp[3],dp[4]) + 1;
        }
    }
    printf("%d\n",max( max( dp[1],dp[2] ),max( dp[3],dp[4] ) ) );
}

递推

Unidirectional TSP(逆推)

链接: 我家大门常打开.
记录路径,所以要逆推,and特判下n==1的情况就ok了

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define ll long long
#define bug(x) cout << #x << " == " << x << '\n'
const ll int MAX_N=2e2+5;
ll dp[MAX_N][MAX_N]= { 0 };
int path[MAX_N][MAX_N]= { 0 };
int a[MAX_N][MAX_N]= { 0 };
int main()
{
    int n,m;
    while(scanf("%d %d",&m,&n)!=EOF)///m行n列
    {
        if(n==1)
        {
            int b[MAX_N]={0};
            ll minn=1e8;
            int index=0;
            for(int i=1;i<=m;i++)
            {
                scanf("%d",&b[i]);
                if(minn>b[i])
                {
                    minn=b[i];
                    index=i;
                }
            }
            printf("%d\n",index);
            printf("%d\n",minn);
        }
        else
        {
            for(int i=1; i<=m; i++)
            {
                for(int j=1; j<=n; j++)
                {
                    scanf("%d", &a[i][j]);

                }
                dp[i][n]=a[i][n];///逆推时间到
            }
            int maxx=1e8;
            int qd=0;
            for(int j=n-1; j>=1; j--)
            {
                for(int i=1; i<=m; i++)
                {
                    dp[i][j]=1e8;
                    int way[7]= {i-1,i,i+1}; ///三种办法走
                    if(i-1==0)
                        way[0]=m;
                    if(i==m)
                        way[2]=1;
                    sort(way,way+3);///字典序嘛
                    for(int k=0; k<3; k++)
                    {
                        int used=dp[way[k]][j+1]+a[i][j];
                        if(used<dp[i][j])
                        {
                            dp[i][j]=used;
                            path[i][j]=way[k];
                        }
                    }
                    if(j==1)
                    {
                        if(maxx>dp[i][1])
                        {
                            maxx=dp[i][1];
                            qd=i;
                        }
                    }
                }
            }
            printf("%d",qd);
            int i=path[qd][1];
            int j=1;
            while(j<n)
            {
                printf(" %d",i);
                j++;
                i=path[i][j];
            }
            printf("\n%d\n",maxx);
        }

    }
}

A Spy in the Metro (逆推)

链接: lalalala.

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define bug(x) cout << #x << " == " << x << '\n'
const ll int MAX_N=2e2+5;
int n,T;
bool has_car[250][60][2]= {0};
int rr[60]= { 0 };
int l[60]= { 0 };
ll dp[250][60] = { 0 };
int a[60]= { 0 };
int cas=0;
void solve()
{
    cas++;
    for(int i=1; i<n; i++)
        dp[T][i]=1e8;///时间T时刻我应该在车站N才对 所以别的地方都是1e8
    dp[T][n]=0;///在T时刻等待的时间为0
    for(int i=T-1; i>=0; i--)
    {
        for(int j=1; j<=n; j++)
        {
            dp[i][j]=dp[i+1][j]+1;///下一秒在呆在这个车站的等待时间
            if(j<n&&has_car[i][j][1]&&i+a[j]<=T)
            {
                dp[i][j]=min(dp[i][j],dp[ i + a[j] ][j+1]);
                ///下一秒上了向右的车子,去了下一站,所以车站的等待时间和i+a[j]时刻在j+1车站相同
            }
            if(j>1&&has_car[i][j][0]&&i+a[j-1]<=T)
            {
                dp[i][j]=min(dp[i][j],dp[ i + a[j-1] ][j-1]);
                ///下一秒上了向左的车......
            }
        }
    }
    if(dp[0][1]>=1e8)
        printf("Case Number %d: impossible\n",cas);
    else
        printf("Case Number %d: %lld\n",cas,dp[0][1]);
}
int main()
{
    while( scanf("%d",&n)!=EOF,n)
    {
        scanf("%d",&T);
        memset(has_car,false,sizeof(has_car));
        for(int i=1; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        a[n]=0;///注意初始化车站两边哦
        a[0]=0;
        int r;
        scanf("%d",&r);///右发的车
        for(int i=1; i<=r; i++)
        {
            scanf("%d",&rr[i]);
            int sum = rr[i];
            for(int j=0; j<n; j++)
            {
                sum+=a[j];
                if(sum>T)
                    break;
                has_car[sum][j+1][1]=1;///在sum时间下,j+1车站有向右开的车子
            }
        }
        int lll;
        scanf("%d",&lll);
        for(int i=1; i<=lll; i++)
        {
            scanf("%d",&l[i]);
            int sum = l[i];
            for(int j=n; j>=1; j--)
            {
                sum+=a[j];
                if(sum>T)
                    break;
                has_car[sum][j][0]=1;
            }
        }
        solve();
    }
}


Color Length

链接: 阿巴阿巴.

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n';
using namespace std;
const ll int MAX_N = 5e3 + 5;
ll dp[MAX_N][MAX_N] = {0};
int tot[MAX_N][MAX_N] = {0}; ///有多少个元素出现还没有取完
string a,b;
int apos_begin[30] = {0};
int apos_endd[30] = {0};
int bpos_begin[30] = {0};
int bpos_endd[30] = {0};
inline int get(int i,int j)
{
    int ans=0;
    for(int k=0; k<26; k++)
    {
        bool flag1=0,flag2=0;
        if(i>=apos_begin[k]||j>=bpos_begin[k])
            flag1=1;
        if(i<apos_endd[k]||j<bpos_endd[k])
            flag2=1;
        if(flag1&&flag2)
            ans++;
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        cin>>a>>b;
        int n=a.length();
        int m=b.length();

        a=' '+a;
        b=' '+b;

        fill(apos_begin,apos_begin+28,INF);
        fill(apos_endd,apos_endd+28,-INF);

        fill(bpos_begin,bpos_begin+28,INF);
        fill(bpos_endd,bpos_endd+28,-INF);

        for(int i=1; i<=n; i++)
        {
            apos_begin[a[i]-'A']=min(i,apos_begin[a[i]-'A']);
            apos_endd[a[i]-'A']=i;

        }

        for(int i=1; i<=m; i++)
        {
            bpos_begin[b[i]-'A']=min(i,bpos_begin[b[i]-'A']);
            bpos_endd[b[i]-'A']=i;
        }

        for(int i=0; i<=n; i++)
        {
            for(int j=0; j<=m; j++)
            {
                dp[i][j]=INF;
            }
        }

        dp[0][0] = 0;
        for(int i=1; i<=n; i++)
        {
            dp[i][0] = dp[i - 1][0] + get(i, 0);
        }

        for(int j=1; j<=m; j++)
            dp[0][j] = dp[0][j - 1] + get(0, j);

        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+get(i,j);
            }
        }
        printf("%lld\n",dp[n][m]);
    }
}

Tour

链接: 巴卡巴卡.
有点累,晚上补补

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define ll long long
#define bug(x) cout << #x << " == " << x << '\n'
const ll int MAX_N=2e2+5;
int n,T;
struct node
{
    double x,y;
}a[MAX_N];
double dp[MAX_N][MAX_N];
double l[MAX_N][MAX_N];
inline double len(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
      int n;
      while(scanf("%d",&n)!=EOF)
      {
          for(int i=1;i<=n;i++)
          {
              scanf("%lf %lf",&a[i].x,&a[i].y);
              for(int j=1;j<=n;j++)
              {
                  dp[i][j]=(double)1e8;
              }
              for(int j=1;j<i;j++)
              {
                  l[i][j]=l[j][i]=len(a[i],a[j]);
              }
          }
          dp[2][1]=l[2][1];
          for(int j=1;j<n-1;j++)
          {
              for(int i=j+1;i<n;i++)
              {
                  dp[i+1][j]=min(dp[i+1][j],dp[i][j]+l[i][i+1]);
                  dp[i+1][i]=min(dp[i+1][i],dp[i][j]+l[i+1][j]);
              }
          }
          double maxx=1e8;
          for(int i=1;i<n-1;i++)
          {
              double tot=dp[n-1][i]+l[n-1][n]+l[i][n];
              maxx=min(maxx,tot);
          }
          printf("%.2lf\n",maxx);
      }
}

区间dp

Cutting Sticks(区间dp)

题目大意:把一个棍子切n次,得到n+1段木棍,求最小的费用,每次花费为棍子原长感觉就是合并石头的逆推版。
所以把棍子拆成n+1份然后合并就可以啦,需要注意的是,
会重复计算一次总长度,所以结果要减去一次总长
(因为最开始的一次合并就是第i段和第i+1段的和,不需要加上a[i+1]-a[i]但是为了方便书写,直接都加上了,会重复多加一个长度)
链接:加油加油.




#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n'
const ll MAX_N =1e6+10;
int a[MAX_N]= {0};
int dp[60][60]= {0};
int main()
{
    int len;
    while(scanf("%d",&len),len)
    {
        int n;
        scanf("%d",&n);
        a[n+1]=len;
        for(int i=0; i<=n+1; i++)
        {
            for(int j=0; j<=n+1; j++)
            {
                dp[i][j]=INF;
            }
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            dp[i-1][i]=a[i]-a[i-1];
        }
        dp[n][n+1]=len-a[n];

        for(int cd=1; cd<=n+1; cd++)
        {
            for(int i=0; i+cd<=n+1; i++)
            {
                int j=i+cd;
                for(int k=i+1; k<j; k++)
                {

                        dp[i][j]=min((dp[i][k]+dp[k][j])+a[j]-a[i],dp[i][j]);
                }
            }
        }
        printf("The minimum cutting is %d.\n",dp[0][n+1]-len);
    }

}

E. Array Shrinking(区间dp)

反思:又是一道区间dp的题目,可惜自己写的的时候只考虑了右边往左边合并的情况于是用了stack来写,但是可能会存在左边往右边合并才是最优的情况,应该把每一段区间的最优解计算出来,这样子才可以算出数组1~n的最优解
链接:可莉真可爱

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n'
const ll MAX_N =5e2+10;
int a[MAX_N] ={0};
int dp[MAX_N][MAX_N] = {0};///dp代表在区间i~j之间压缩的最小长度
int s[MAX_N][MAX_N] = {0};///s等于在区间i~j之间压缩出来的值
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i][i]=a[i];
        for(int j=i;j<=n;j++)
        {
            dp[i][j]=j-i+1;///一开始在区间i~j之间的长度
        }
    }
    for(int l=1;l<=n;l++)///区间长度
    {
        for(int i=1;i+l<=n;i++)///区间起点
        {
            int j=i+l;
            for(int k=i;k<j;k++)
            {
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
                if( dp[i][k]==1 && dp[k+1][j]==1 && s[i][k]==s[k+1][j] )///说明左边可以合并成为一个元素,右边同
              且左边合并出来的元素等于右边
                {
                    s[i][j] = s[i][k]+1;
                    dp[i][j] = 1;
                }
            }
        }
    }
    printf("%d\n",dp[1][n]);
}

[SCOI2003]字符串折叠

链接:题意真的坑
一开始都没有看懂样例解释
NEERCYESYESYESNEERCYESYESYES
等价于:2( N E E R 3 ( Y E S ) )一共有14个字符(加数学和字母,括号)
可以看出来EE=2(E)但是反而没有不合并的时候优 所以都是要比较的
很容易想到区间dp将大问题转化为一个个小问题
!!!!循环节的判断是重点 代码如下:

#include <bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << endl;
typedef pair<int, int> ppp;
const int INF = 0x3f3f3f3f;
const int MAX_N = 1e2 + 10;
#define ll long long
string a;
bool check(int l, int r, int len) //截取的区间为[l,r],循环节为len
{
    for (int i = l; i <= r; i++)
    {
        if (a[i] != a[(i - l) % len + l])
        {
            return false;
        }
    }
    return true;
}
int num[120] = {0};
int dp[120][120] = {0};
signed main()
{
    cin >> a;
    int n = a.length();
    a = " " + a;
    for (int i = 1; i <= 9; i++)
        num[i] = 1;
    for (int i = 10; i <= 99; i++)
        num[i] = 2;
    num[100] = 3; ///三位数的字符串占位有三个,上同理

    memset(dp, INF, sizeof(dp));
    for (int i = 1; i <= n; i++)
    {
        dp[i][i] = 1; 预处理:区间[i,i]只会有一个字母
    }
    for (int cd = 2; cd <= n; cd++) ///区间dp
    {
        for (int l = 1; l + cd - 1 <= n; l++)
        {
            int r = l + cd - 1;
            for (int k = l; k < r; k++)
            {
                dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
                ///左右两个区间的简单合并
                int len = k - l + 1; ///循环节大小
                if (cd % len == 0)   ///区间长度刚刚好可以分成d个循环节份
                {
                    if (check(l, r, len))
                    {
                        dp[l][r] = min(dp[l][r], dp[l][k] + 2 + num[cd / len]);
                        ///有两个括号所以加2,然后添加的数字占num[cd/len];
                        ///dp[l][k]刚好代表了循环节的最短长度
                    }
                }
            }
        }
    }
    printf("%d\n", dp[1][n]);
    system("pause");
}

树状DP

Another Crisis

链接:好想打游戏
题目大意:有一个老板n个员工组成的树状结构,每一个员工,工人都只有唯一的直属上司,且工人没有下属,每一个上司只有不少于T%的直属下属交了请愿书,他才会上交给他的直属上司,现在要求最少多少工人交了请愿书,老板才会看见。
INPOT:
n个员工,输入百分比T
然后输入每一个员工的直属上司

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n'
const ll MAX_N =1e5+10;
vector<int> sonn[MAX_N];
int n,T;
int dp(int u)///头结点
{
    if(sonn[u].empty())
        return 1;///这是一个没有下属的最底层员工
    int k=sonn[u].size();
    vector<int> d;
    for(int i=0; i<k; i++)
    {
        d.push_back( dp( sonn[u][i] ) );
    }
    sort( d.begin(), d.end() ); ///排序取最小的需要员工数目
    int need=(k*T-1)/100+1;///u员工会上报给上级所需要的最低直接下属数目
    int ans=0;
    for(int i=0; i<need; i++)
    {
        ans+=d[i];///u员工会上报给上级所需要的最低员工数目
    }
    return ans;
}
int main()
{

    while(scanf("%d %d",&n,&T)!=EOF,n,T)
    {
        int b;for(int i=1; i<=n; i++)
        {

            scanf("%d",&b);
            sonn[b].push_back(i);
        }

        printf("%d\n", dp(0) );

        for(int i=0; i<=n; i++)
        {
            sonn[i].clear();
        }

    }
}


[CTSC1997]选课

去洛谷搜吧,懒得放链接了
设一个虚假的头节点遍历所有的办法就很nice!!

#include <bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << endl;
typedef pair<int, int> ppp;
const int INF = 0x3f3f3f3f;
const int MAX_N = 2e3 + 10;
#define ll long long
struct node
{
    int u,v,next;
}e[MAX_N];

int head[MAX_N] = { 0 };
int tot=0;
inline void add_edge(int u,int v)
{
    tot++;
    e[tot].u=u;
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot;
}

int n,m;
int f[MAX_N][MAX_N] = { 0 };
void dp(int u)
{
    for(int i=head[u]; i; i=e[i].next)
    {
        int v=e[i].v;
        dp(v);
        for(int j=m+1;j>=1;j--)///j代表选j门课,因为每一步都是从0开始的 所以是m+1
        {
            for(int k=0;k<j;k++)
            {
                f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
                ///u是选的第一门课,就是头结点,u;里面选的课是含有v的,所以可以以v为头结点再次拓展
            }
        }
    }
}
int main()
{

    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int u,w;
        scanf("%d %d",&u,&w);
        f[i][1] = w;
        add_edge(u,i);
    }
    dp(0);///每一个的没有直接选修课的都可以作为头结点,一开始的操作 将0和这些点点作为单项边连起来了
    printf("%d\n",f[0][m+1]);
}

poj 2152 Fire

好难 不会 占个坑

Computer(求树的直径,求点的最大距离的好办法)

链接:敲码不易 记得点赞 球球了QWQ

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define  P pair <int,int>
using namespace std;
#define bug(x) cout<<#x<<"=="<<x<<endl;
const ll int maxn=1e5+10;
int head[maxn] = { 0 };
ll dis[maxn] = { 0 };
int tot = 0;
ll max_len = 0;
int s = 0;
struct node
{
    int x;
    int y;
    ll w;
    int next;
} way[maxn];
void add_edge(int u,int v,ll w)
{
    tot++;
    way[tot].x = u;
    way[tot].y = v;
    way[tot].w = w;
    way[tot].next = head[u];
    head[u] = tot;
}
void dfs(int u,int fu,ll len)
{
    if(len>=max_len)
    {
        max_len = len;
        s = u;
    }
    for(int i=head[u]; i; i=way[i].next)
    {
        int v=way[i].y;
        if(v!=fu)
        {
            dfs(v,u,len+way[i].w);
            dis[v]=max(dis[v],len+way[i].w);
        }
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset( head,0,sizeof(head) );
        memset(dis,0,sizeof(dis));
        tot=0;
        for(int i=2; i<=n; i++)
        {
            int v;
            ll w;
            scanf("%d %lld",&v,&w);
            add_edge(i,v,w);
            add_edge(v,i,w);
        }
        max_len=0;
        s = 0;
        dfs(1,0,0);
        ///此时求出来的s就是树的直径的一个端点
        ///因为如果1在树的直径上,遍历出来的s肯定是树得到直径的端点
        ///如果1不在树的直径上,最后遍历出来的也是直径的端点的,画图可以画出来
        dfs(s,0,0);///树的直径的起点开始遍历
        ///只需要dfs两遍就可以求出树的直径啦
        dfs(s,0,0);///树的直径的终点开始遍历
        for(int i=1; i<=n; i++)
        {
            printf("%lld\n",dis[i]);
        }
    }
}

UVA1218 完美的服务 Perfect Services

上洛谷找题即可

几个状态真的好难想 还有怎么转移 难的QwQ只能多加练习了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=1e4+10;
const ll int INF=1e5+10;
int n;
int tot=0;
vector<int> way[maxn];
ll dp[maxn][3] = { 0 };
///dp[j][0]为j为服务器,每个子结点可以是服务器也可以不是服务器
///dp[j][1]为j不是服务器,但是j的父亲是服务器,这意味着j的所有子结点都不是服务器
///题目要求 u 所连接的所有点中有且仅有一台服务器
///dp[j][2]u和u的父亲都不是服务器。这意味着u恰好有一个儿子是服务器。
void dfs(int u,int fu)
{
    dp[u][0] = 1;

    for(auto v:way[u])
    {
        if(v!=fu)
        {
            dfs(v,u);
            dp[u][0] +=min(dp[v][0],dp[v][1]);
            ///该点为服务器,所以每个子结点可以是服务器也可以不是服务器
            dp[u][1] += dp[v][2];///u的所有子结点v都不是服务器,而且自己也不是服务器
        }
    }
    dp[u][2] = INF;
    for(auto v:way[u])
    {
        if(v!=fu)
        {
            dp[u][2]=min(dp[u][2],dp[u][1]-dp[v][2]+dp[v][0]);
            ///选择v为服务器,所以是其他所有的子结点都是dp[vvv][2]的状态,只有v是dp[v][0]的状态
        }
    }
}
int main()
{
    int u,v;
    while(scanf("%d",&n)!=EOF)
    {
        tot = 0;
        for(int i=1;i<=n;i++)
            way[i].clear();
        for(int i=1;i<n;i++)
        {
            scanf("%d %d",&u,&v);
            way[u].push_back(v);
            way[v].push_back(u);

            dp[i][0] = 0;
            dp[i][1] = 0;
            dp[i][2] = 0;
        }
        dp[n][0] = 0;
        dp[n][1] = 0;
        dp[n][2] = 0;

        dfs(1,0);
        printf("%lld\n", min(dp[1][0],dp[1][2]) );
        ///根结点不存在父亲结点
        int now;
        scanf("%d",&now);
        if(now==-1)break;
    }
}

0-1tree(难题,难惹,要考虑很多状态)

链接:写了好久好久( ̄▽ ̄)

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#define  P pair <int,int>
using namespace std;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int tot = 0;
const ll int maxn = 1e5+10;
int dp[maxn][4] = { 0 };
///dp[i][0]以i为起点,所有子路径全为0的个数
///dp[i][1]以i为起点,所有子路径先0后1的个数
///dp[i][2]以i为起点,所有子路径先1后0的个数
///dp[i][3]以i为起点,所有子路径都为1的个数
int head[maxn] = { 0 };
struct node
{
    int x;
    int y;
    int next;
    int w;
} way[maxn];
inline void add_edge(int u,int v,int w)
{
    tot++;
    way[tot].x = u;
    way[tot].y = v;
    way[tot].w = w;
    way[tot].next = head[u];
    head[u] = tot;
}
ll sum[4] = { 0 };
ll ans = 0;
void dfs(int x,int fx)
{
    for(int i=head[x]; i; i=way[i].next)
    {
        int y = way[i].y;
        int w = way[i].w;
        if(y != fx)
        {
            dfs(y,x);
            if(w==0)
            {
                sum[0] = dp[y][0] + 1;
                sum[1] = dp[y][1] + dp[y][3];
                sum[2] = sum[3] = 0;
            }
            else
            {
                sum[2] = dp[y][0] + dp[y][2];
                sum[3] = dp[y][3] + 1;
                sum[0] = sum[1] = 0;
            }
            ans += 2*sum[0]*dp[x][0];
            ans += sum[0]*dp[x][3];
            ans += sum[0]*dp[x][1];
            ans += sum[1]*dp[x][0];
            ans += sum[2]*dp[x][3];
            ans += sum[3]*dp[x][0];
            ans += sum[3]*dp[x][2];
            ans += 2*sum[3]*dp[x][3];
            for(int i=0; i<4; ++i)
                dp[x][i]+=sum[i];
        }
    }
    ans += (dp[x][0]*2+dp[x][1]+dp[x][2]+dp[x][3]*2);
}
int main( )
{
    int n;
    cin>>n;
    for(int i=1; i<n; i++)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    dfs(1,0);
    cout<<ans<<endl;
}

Starship Troopers

#include <bits/stdc++.h>
using namespace std;
const int maxn=105;
int n,m,len;
int dp[maxn][maxn];//dp表示在i点放j人能得到的能量
int bug[maxn],p[maxn],vis[maxn],head[maxn];

struct node
{
    int now,next;
} tree[maxn*2];

void add(int x,int y)
{
    tree[len].now = y;
    tree[len].next = head[x];
    head[x] = len++;
}

void dfs(int root)
{
    int cost,i,j,k,son;
    vis[root] = 1;
    cost = (bug[root]+19)/20;
    for(i = cost; i<=m; i++)
        dp[root][i] = p[root];
    for(i = head[root]; i!=-1; i = tree[i].next)
    {
        son = tree[i].now;
        if(!vis[son])
        {
            dfs(son);
            for(j = m; j>=cost; j--)
            {
                for(k = 1; j+k<=m; k++)
                {
                    if(dp[son][k])
                        dp[root][j+k] = max(dp[root][j+k],dp[root][j]+dp[son][k]);
                }
            }
        }
    }
}

int main()
{
    int i,j,x,y;
    while(~scanf("%d%d",&n,&m),n+m>0)
    {
        for(i = 1; i<=n; i++)
        {
             scanf("%d%d",&bug[i],&p[i]);
             vis[i] = 0;
             head[i] = -1;
        }

        len = 0;
        for(i = 1; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        if(!m)
        {
            printf("0\n");
            continue;
        }
        memset(dp,0,sizeof(dp));
        dfs(1);
        printf("%d\n",dp[1][m]);
    }

    return 0;
}

优化dp

Making the Grade(优化dp,记录中间值)

①Making the Grade
链接: 加油加油加油!!!.
dp[i][j]=dp[i-1] [k] + min(a[i]-b[j]);(1<=k<=j)
dp[i][j]代表了1 ~ i中最后一个楼梯高度是b[j] 的最小花费,所以我们需要求出前面dp[i-1][1] ~ dp[i-1][j]的最小花费,在第二个循环中比较即可,用minn标记dp[i-1][1~(j-1)] 的最小花费。是一个很不错的办法
还学到了一个新函数去重函数unique。zbc

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define ll long long
#define bug(x) cout << #x << " == " << x << '\n'
const ll int MAX_N=2e3+5;
ll a[MAX_N] = { 0 };
ll b[MAX_N] = { 0 };
ll dp[MAX_N][MAX_N] = { 0 };
int main()
{
   int n;
   scanf("%d" ,&n);
   for(int i=1;i<=n;i++)
   {
       scanf("%lld" ,&a[i]);
       b[i]=a[i];
   }
   sort(b+1,b+1+n);
   int m=unique(b+1,b+1+n)-(b+1);///去重
   for(int i=1;i<=n;i++)///前i-1个数都已经完成计算啦,接下来我们要计算a[i]那个地方啦
   {
       ll minn=INT_MAX;
       for(int j=1;j<=m;j++)
       {
           minn=min(minn,dp[i-1][j]);
           ///dp[i-1][j]是前面i-1个数,第i-1个数更改为b[j]时候,求出来的最小花费
           ///要和前面的dp[i-1][1]~dp[i-1][j-1]做一个比较哦!!!不然就会浪费钱~
           dp[i][j]=minn+abs(a[i]-b[j]);///a[i]修改到b[i]啦
       }
   }
   ll maxx=INT_MAX;
   for(int i=1;i<=m;i++)
   {
       maxx=min(maxx,dp[n][i]);///当然要比较下a[n]修改到哪个值最划算啦
   }
   printf("%lld\n",maxx);
}

状压

cf1042BVitamins

①CF1042B Vitamins:一个简单的dp加二进制状压的题目~~
链接: 哎惹.
大意:数据有n组数,每组数有一个价值cic_ici​和一个字符串S,字符串S中包含3个字母A,B,C,问集齐ABC三个字母的最小价值(一个字母可以有多个)
ps:由于很简单,是可以记录数据之类的做出来的,但是要练习dp嘛,所以写了下dp的写法
利用二进制表示不同的状态
001 C
010 B
011 BC
100 A
101 AC
110 AB
111 ABC
dp的数组大小开7(111)就可以啦
dp [i] [j] =max(d[i-1] [j],d[i-1][j-tmp] + a[tmp].value );
j表示当前状态(利用二进制表示)
i表示前i组数
我们可以通过&运算来判断是否能通过tmp到达j这个状态,
&运算:https://blog.csdn.net/xiaopihaierletian/article/details/78162863这个很清楚
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;

即:两位同时为“1”,结果才为“1”,否则为0
eg:101(AC)&100(A)!=0 而100—>101是可以的…
而且末状态的数值一定大于初状态:101>100
上代码啦~

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << '\n'
const int MAX_N=1e3+5;
#define ll long long
struct node
{
    int value;
    string s;
    int A,B,C;
} a[MAX_N];
int main()
{
    int n;
    scanf("%d",&n);
    int dp[1<<3]= {0};
    fill(dp,dp+(1<<3),1e8);
    for(int i=1; i<=n; i++)
    {
        cin>>a[i].value>>a[i].s;
        a[i].A=0;
        a[i].B=0;
        a[i].C=0;
        if(a[i].s.find('A')!=-1)
        {
            a[i].A=1;

        }
        if(a[i].s.find('B')!=-1)
        {
            a[i].B=1;

        }
        if(a[i].s.find('C')!=-1)
        {
            a[i].C=1;
        }
    }
    dp[0]=0;
    for(int j=1; j<=n; j++)
    {
        for(int i=7; i>=1; i--)
        {
             /*int tmp=0;

            if(a[j].A==1)
            {
                tmp+=4;
            }
            if(a[j].B==1)
            {
                tmp+=2;
            }
            if(a[j].C==1)
            {
                tmp+=1;
            }
            if(!(tmp&i))continue;
            if(i>=tmp)
            dp[i]=min(dp[i],dp[i-tmp]+a[j].value);

            if(a[j].C==1)///001
            {
                if((1&i)&&i>=1)
                    dp[i]=min(dp[i],dp[i-1]+a[j].value);
            }
            if(a[j].B==1)///010
            {
                if((2&i)&&i>=2)
                    dp[i]=min(dp[i],dp[i-2]+a[j].value);
            }*/
            if(a[j].B&&a[j].C)///011
            {
                if((3&i)&&i>=3)
                    dp[i]=min(dp[i],dp[i-3]+a[j].value);
            }
            if(a[j].A==1)///100
            {
                if((4&i)&&i>=4)
                    dp[i]=min(dp[i],dp[i-4]+a[j].value);
            }
            if(a[j].A&&a[j].C)///101
            {
                if((5&i)&&i>=5)
                    dp[i]=min(dp[i],dp[i-5]+a[j].value);
            }
            if(a[j].A&&a[j].B)///110
            {
                if((6&i)&&i>=6)
                    dp[i]=min(dp[i],dp[i-6]+a[j].value);
            }
            if(a[j].A&&a[j].B&&a[j].C)///111
            {
                if((7&i)&&i>=7)
                    dp[i]=min(dp[i],dp[i-7]+a[j].value);
            }
        }
    }
    if(dp[7]==1e8)
    {
       printf("-1\n");
    }
    else
        printf("%d\n",dp[7]);
}

被抹掉的代码是只能求出x个字符串组合刚好是ABC
比如AB+C,A+B+C之类的。那种AB+BC的一起组合就求不出来了,所以换了下代码

P1433 吃奶酪

第二题来啦!!!!
②P1433 吃奶酪
链接: lalala.
题目描述
房间里放着 nnn 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 (0,0)(0,0)点处。
输入格式
第一行有一个整数,表示奶酪的数量 n。
第 2到第 (n+1) 行,每行两个实数,第 (i+1) 行的实数分别表示第 i块奶酪的横纵坐标 xi​,yi​。
输出格式
输出一行一个实数,表示要跑的最少距离,保留 2位小数。

#include<bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << '\n'
const int MAX_N=1e3+5;
#define ll long long
struct node
{
    double x,y;
} a[MAX_N];
double len(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dp[16][1<<16]= {0};
double way[20][20];
int n;
int main()
{
    scanf("%d",&n);
    a[0].x=0;
    a[0].y=0;
    for(int i=1; i<=n; i++)
    {
        scanf("%lf %lf",&a[i].x,&a[i].y);
    }
    memset(dp,127,sizeof(dp));///将浮点数dp最大化
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<i; j++)
        {
            way[j][i] = len(a[i],a[j]);
            way[i][j] = way[j][i];
        }
    }
    for(int i=1; i<=n; i++)
    {
        dp[i][1<<(i-1)]=way[0][i];///从0到i的距离就是1<<(i-1)状态的最小路径
    }
    int N=n;
    double maxx=INT_MAX;
    for(int k=1; k<(1<<N); k++) //k表示当前状态
    {
        for(int i=1; i<=N; i++)///i表示当前状态的终点
        {
            if((k&(1<<(i-1)))==0)
                continue;
            for(int j=1; j<=N; j++)///j表示当前状态的上一个状态k-(1<<(i-1))的终点
            {
                if(i==j)
                    continue;
                if((k&(1<<(j-1)))==0)
                    continue;
                dp[i][k]=min(dp[i][k],dp[j][k-(1<<(i-1))]+way[i][j]);

            }
            if(k==((1<<n)-1))
            {
                maxx=min(maxx,dp[i][k]);
            }
        }
    }
    printf("%.2lf\n",maxx);
}

P5911 [POI2004]PRZ

第三题也来啦!!!P5911 [POI2004]PRZ
链接:看到这里了 还不收藏就是白嫖行为 谴责谴责.
题目背景

一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥。
题目描述:
桥已经很旧了, 所以它不能承受太重的东西。任何时候队伍在桥上的人都不能超过一定的限制。 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过。队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少。
输入格式:
第一行两个数: W 表示桥能承受的最大重量和 n 表示队员总数。
接下来 n 行:每行两个数: t 表示该队员过桥所需时间和 w 表示该队员的重量。
输出格式:
输出一个数表示最少的过桥时间。

#include<bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << '\n'
const int MAX_N=1e5+5;
#define ll long long
struct node
{
    int t,w;
    bool operator <(node a)
    {
        if(w!=a.w)
        {
            return w<a.w;
        }
        return t<a.t;
    }
}a[40];
int dp[MAX_N]={0};
int tt[MAX_N]={0};
int ww[MAX_N]={0};
int main()
{
    int w,n;
    scanf("%d %d",&w,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a[i].t,&a[i].w);
    }
    int tot=((1<<n));
    for(int i=1;i<tot;i++)///每一种状态的枚举
    {
        for(int j=1;j<=n;j++)///判断队员j是不是在这个状态里面????
        {
            if(((1<<(j-1))&i))///如果在这个状态里面的话
            {
                tt[i]=max(tt[i],a[j].t);///找出这个状态里的队员他们算一个组的时候需要的最大时间
                ww[i]+=a[j].w;///这个状态下队员的总重量
            }
        }
    }
    memset(dp,10000,sizeof(dp));
    dp[0]=0;///没有队员的时候时间是0
    for(int i=1;i<tot;i++)
    {
        for(int j=i;;j=i&(j-1))///可以通过这个样子枚举i状态的每一个子状态,不明觉厉!!!
        {
            if(ww[i^j]<=w)///(i^j)^j=i且0^1=1,1^0=1,0^0=1,1^1=0所以i^j指的是在i状态存在但是在j状态下不存在的队员
            {
                dp[i]=min(dp[i],dp[j]+tt[i^j]);///tt[i^j]其他队员组一个组过桥的时间
            }
            if(j==0)break;///枚举完啦
        }
    }
    printf("%d\n",dp[tot-1]);
}

1.关于for( j=i ; ; j = i & (j-1) )
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
e.g:i=1011;
j=1011;
j=1011&1010=1010;
j=1011&1001=1001;
j=1011&1000=1000;
j=1011&0111=0011;
j=1011&0010=0010;
j=1011&0001=0001;
枚举了每一种子状态
2.关于(i ^ j) ^ j = i非常好用,因为异或运算满足自反性。
第四题:P3052 [USACO12MAR]Cows in a Skyscraper G

P3052 [USACO12MAR]Cows in a Skyscraper G

链接:收藏一下吧~.

#include<bits/stdc++.h>
using namespace std;
#define bug(x) cout << #x << " == " << x << '\n'
const int MAX_N=1e5+5;
#define ll long long
int w[30]= {0};
ll dp[1<<19]= {0};///每一种状态下的电梯数
int reminder[1<<19];///该状态下 所有电梯的最大剩余量
int main()
{
    int n,maxx;
    scanf("%d%d",&n,&maxx);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&w[i]);
    }
    int tot=1<<n;
    fill(dp,dp+(1<<n),1e6);
    dp[0]=0;
    reminder[0]=maxx;///状态为0的时候,一只牛都没有哦,所以要初始化一下子
    for(int i=1;i<tot;i++)///又开始枚举每一种状态啦
    {
        for(int j=1;j<=n;j++)///塞奶牛
        {
            int now=i|(1<<(j-1));
            ///i状态加上j号牛now状态啦,|运算0|1=1,1|0=1,1|1=1,0|0=0
            if(reminder[i]>=w[j]&&dp[now]>=dp[i])
            {
                reminder[now]=max(reminder[i]-w[j],reminder[i]);
                dp[now]=dp[i];
            }
            else if(reminder[i]<w[j]&&dp[now]>=dp[i]+1)
            {
                reminder[now]=max(reminder[i],maxx-w[j]);
                dp[now]=dp[i]+1;
            }
        }
    }
    printf("%lld\n",dp[tot-1]);
}

ps:位运算真的好好用,这一题就利用了位运算的|通过这个枚举出来每一个状态的下一个状态,不得不说非常机智啦,之前写的每一个状态去找上一个状态和他也是异曲同工之妙,但是我还需要判断一下,这个就完全不用啦。
一开始卡测试点发现是因为可能会存在一个状态但是记录的reminder不是最小的情况,修改后在dp[i]相同的情况下也要修改reminder[i]的值使它变成当前状态的最大电梯剩余量然后就快乐ac啦开心!!!!

数位dp

(HDU)不要62(板子)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn =30;
int a[maxn] = { 0 };
///a代表每一位的数
ll dp[maxn][2] = { 0 };
///i代表位数
int pos = 0;
ll dfs(int pos,int pre,int state,int limit)
{
    if(!pos)return 1;
    if( (!limit) && dp[pos][state]!=-1)
        return dp[pos][state];
    ///如果没有限制条件,直接返回dp即可;
    int up = 9;///该位0~9的枚举
    if(limit)
    {
        up = a[pos];///该位只能枚举到0~a[pos]
    }
    ll ans = 0;
    for(int i=0;i<=up;i++)
    {
        if(pre==6&&i==2)continue;
        if(i==4)continue;

        ans += dfs(pos-1,i,i==6,limit&&i==a[pos]);
        ///如果上一位是限制的,这一位也是到限制临界点,那么下一位也是限制的
    }
    if(!limit)dp[pos][state]=ans;
    ///dp专门存储没有限制的值
    return ans;
}
ll slove(int x)
{
    pos=0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x/=10;
    }
    return dfs(pos,-1,0,true);
    ///哪一位,前面一位的值,前面一位是不是6,有没有限制条件
}
int main()
{
    int l,r;
    memset( dp,-1,sizeof(dp) );
    while( scanf("%d %d",&l,&r) && (l+r) )
    {
        printf( "%lld\n",slove(r)-slove(l-1) );
    }
}

(HDU)F(x)

///发现,数位dp往往是根据条件,修改dfs需要的变量定义
///而dp是用来存储没有区间限制条件的计算出来的值
///limit是用来判断区间,防止计算的值超过了区间,是一个模板了
///面对数位问题,重点是通过条件,准确地构建方程还有适当优化
///在这一题中,如果dp[maxn][sum][maxx]肯定会超出内存
///这个时候,发现该题目只需要sum<=maxx的那一部分,因为这是题目条件
///因此dp降到了二维

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn = 12;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int a[maxn] = { 0 };
///a代表每一位的数
ll dp[maxn][N] = { 0 };
///i代表位数
int pos = 0;
ll maxx = 0;

inline ll f(ll x)
{
    if(!x)return 0;
    return f(x/10)*2+x%10;
}
ll dfs(int pos,ll sum,bool limit)
{
    if(maxx-sum<0)return 0;
    ///当前数超过了f(a)不能取
    if(!pos)
    {
        return sum<=maxx;
        ///每一位都取过了,判断是不是满足条件即可
    }
    if(!limit&& (dp[pos][maxx-sum]!=-1) )
    {
        return dp[pos][maxx-sum];
    }
    int up=9;
    if(limit)
    {
        up=a[pos];
    }///板子,防止区间过界
    ll ans=0;
    for(int i=0; i<=up; i++)
    {
        ans+=dfs(pos-1,sum+i*( 1<<(pos-1) ),limit&&i==a[pos]);
    }
    if(!limit)
    {
        dp[pos][maxx-sum]=ans;
    }
    return ans;
}
ll slove(int x)
{
    pos=0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x/=10;
    }
    return dfs(pos,0,true);
    ///第二个表示前面花费了sum
}
int main()
{
    ll a,b;
    int T_T;
    scanf("%d ",&T_T);
    int cas=0;
    memset( dp,-1,sizeof(dp) );
    while(T_T--)
    {
        scanf("%lld %lld",&a,&b);
        cas++;
        maxx=f(a);
        printf( "Case #%d: %lld\n",cas,slove(b));
    }
}

[USACO06NOV] Round Numbers S

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int a[maxn] = { 0 };
int pos=0;
ll dp[maxn][70]= {0};
ll dfs(int pos,int sum,bool qd0,bool limit)
{
    if(!pos)
    {
        return sum>=32;
    }
    ///有无前导0,我存储的dp应该是没有前导0的
    if( !limit && !qd0 && dp[pos][sum]!=-1)
    {
        return dp[pos][sum];
    }
    int up=1;
    if(limit)
    {
        up = a[pos];
    }
    ll ans = 0;
    for(int i=0; i<=up; i++)
    {
        if(i==0&&qd0)
        {
            ans += dfs(pos-1,sum,true,limit&&i==a[pos]);
            ///有前导0的时候,统计的个数不改变
        }
        else if(i==1)
        {
            ans += dfs(pos-1,sum-1,false,limit&&i==a[pos]);
        }
        else if(i==0)
        {
            ans += dfs(pos-1,sum+1,false,limit&&i==a[pos]);
        }
    }
    if( !limit && !qd0 )
    {
        dp[pos][sum]=ans;
    }
    return ans;
}
ll slove(ll x)
{
    pos=0;
    while(x)
    {
        pos++;
        a[pos] = x%2;
        x /= 2;
    }
    return dfs(pos,32,true,true);
    ///中间值代表sum0-sum1可能会存在负数,所以加上32
}
int main()
{
    ll r,l;
    scanf("%lld %lld",&l,&r);
    memset( dp,-1,sizeof(dp) );
    cout<<( slove(r)-slove(l-1) )<<endl;
}

(HDU)Balanced Number

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int pos=0;
int a[maxn]  = { 0 };
ll dp[20][2000][20];
///i代表当前位,j代表sum,
ll dfs(int pos,int sum,int cen,bool limit)
{
    if(!pos)
    {
        return sum==0;
    }
    if( !limit && dp[pos][sum][cen]!=-1)
    {
        return dp[pos][sum][cen];
    }
    ll up=9;
    if(limit)
    {
        up=a[pos];
    }
    ll ans = 0;
    for(int i=0;i<=up;i++)
    {
        ans += dfs(pos-1,sum+(pos-cen)*i,cen,limit&&i==a[pos]);
    }
    if(!limit)
    {
        dp[pos][sum][cen]=ans;
    }
    return ans;
}
ll slove(ll x)
{
    if(x==-1)return 0;
    pos = 0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x /= 10;
    }
    ll ans = 0;
    for(int i=1;i<=pos;i++)
    {
        ans += dfs(pos,0,i,true);
        ///i为跷跷板的中点
        ///0为(左边跷跷板的值-右跷跷板的值)
        ///pos为当前位数
    }
    return ans-(pos-1);
    ///因为每一次计算会把 0,00,000,0000....当成不同的数进行记录
    ///所以需要减去这些多余的值
}
int main()
{
    int T_T;
    scanf("%d",&T_T);
    ll l,r;
    memset( dp,-1,sizeof(dp) );
    while(T_T--)
    {
        scanf("%lld %lld",&l,&r);
        printf("%lld\n", slove(r)-slove(l-1) );
    }
}

(cf)55D

链接:https://codeforces.com/problemset/problem/55/D

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int pos=0;
const ll mod=2520;
///因为1~9的最小公倍数为5040
int a[30]= {0};
ll dp[30][2530][50]= {0};
int yz[2530] = { 0 };

///j代表前面n-pos位每一位相加得到的值%mod;
///2520最多可以分为48个因子
///最后只需要判断j%yz[k]==0即可
inline void js()
{
    int  tot = 0;
    for(int i=1; i<=2520; i++)
    {
        if(2520%i==0)
        {
            tot++;
            yz[i] = tot;
        }
    }
}
inline int _lcm(int a,int b)
{
    return a*b/__gcd(a,b);
}
ll dfs(int pos,int he,int lcm,bool limit)
{

    if(!pos)
    {
        return he%lcm==0;
    }
    if(!limit&dp[pos][he][ yz[lcm] ]!=-1)
    {
        return dp[pos][he][ yz[lcm] ];
    }
    int up=9;
    if(limit)
    {
        up=a[pos];
    }
    ll ans = 0;
    for(int i=0; i<=up; i++)
    {
        int nextlcm=lcm;
        if(i)
            nextlcm = _lcm(lcm,i);
        int nexthe=(he*10+i)%mod;
        ans += dfs(pos-1,nexthe,nextlcm,limit&&i==a[pos]);
    }
    if(!limit)
    {
        dp[pos][he][ yz[lcm] ]=ans;
    }
    return ans;
}
ll slove(ll x)
{
    pos=0;
    while(x)
    {
        pos++;
        a[pos]=x%10;
        x/=10;
    }
    return dfs(pos,0,1,true);
}
int main()
{
    js();
    int T_T;
    scanf("%d",&T_T);
    memset(dp,-1,sizeof(dp) );
    ll l,r;
    while(T_T--)
    {

        cin>>l>>r;
        cout<<slove(r)-slove(l-1)<<'\n';
    }
}

Bomb

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int a[maxn] = { 0 };
ll dp[maxn][10][2] = { 0 };
int pos=0;
ll dfs(int pos,int pre,bool yep,bool limit)
{
    if(pos==0)
    {
        return yep==1;
    }
    if((!limit)&&(dp[pos][pre][yep]!=-1))
    {
        return dp[pos][pre][yep];
    }
    int up=9;
    if(limit)
    {
        up=a[pos];
    }
    ll ans=0;
    for(int i=0; i<=up; i++)
    {
        ans+=dfs(pos-1,i,(pre==4&&i==9)||yep,limit&&i==a[pos]);
    }
    if(!limit)
    {
        dp[pos][pre][yep]=ans;
    }
    return ans;
}
ll slove(ll x)
{
    if(x==0)return 0;
    pos = 0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x /= 10;
    }
   ll ans=dfs(pos,0,false,true);
    return ans;
}
signed main()
{
    ll r;
    int T_T;
    scanf("%d",&T_T);
    memset(dp,-1,sizeof(dp));
    while(T_T--)
    {
        cin>>r;
        cout<<slove(r)<<'\n';
    }
}

(HDU)B-number

和上面同理就加了一个维度

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
int a[maxn] = { 0 };
ll dp[maxn][20][10][2] = { 0 };
int pos=0;
ll dfs(int pos,int pre,int res,bool yep,bool limit)
{
    if(pos==0)
    {
        if(res%13==0)
        return yep==1;
        else
        return 0;
    }
    if((!limit)&&(dp[pos][res][pre][yep]!=-1))
    {
        return dp[pos][res][pre][yep];
    }
    int up=9;
    if(limit)
    {
        up=a[pos];
    }
    ll ans=0;
    for(int i=0; i<=up; i++)
    {
        ans+=dfs(pos-1,i,(res*10+i)%13,(pre==1&&i==3)||yep,limit&&i==a[pos]);
    }
    if(!limit)
    {
        dp[pos][res][pre][yep]=ans;
    }
    return ans;
}
ll slove(ll x)
{
    if(x==0)return 0;
    pos = 0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x /= 10;
    }
   ll ans=dfs(pos,0,0,false,true);
    return ans;
}
signed main()
{
    ll r;
    ///int T_T;
    ///scanf("%d",&T_T);
    memset(dp,-1,sizeof(dp));
    while(T_T--)
    while(cin>>r&&r)
    {
        cout<<slove(r)<<'\n';
    }
}


(HDU)XHXJ’s LIS

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll int maxn = 120;
const ll int N = 1e5+10;
#define bug(x) cout<<#x<<"=="<<x<<endl;
ll dp[30][3000][30] = { 0 };
int a[30] = { 0 };
int pos = 0;
int k;
inline bool check(int x)
{
    bitset<15> bits = x;
    return bits.count()==k;
    ///数组里面有多少个1
}
inline int get_next(int state,int x)///两个等效
{
    for(int i=x;i<=9;i++)
    {
        if((1<<i)&state)
        {
            return ( state^(1<<i) )|(1<<x);
        }
    }
    return state|(1<<x);
}
/*inline int get_next(int state,int now)
{
    bitset<15> bits=state;
    int i=now;
    for(; i<=9; i++)
    {
        if(bits[i]==1)
        {
            break;
        }
    }
    if(i!=10)
    {
        bits[i] = 0;
    }
    bits[now]=1;
    ///修改最长序列的末尾值
    int ans=bits.to_ulong();
    ///讲二进制的数组转换成unsigned long的整数型
    return ans;
}*/
ll dfs(int pos,int state,bool lead,bool limit)
{
    if(!pos)  return check(state);
    if( !limit&&dp[pos][state][k]!=-1)  return dp[pos][state][k];

    int up = limit ? a[pos]:9 ;
    ///上下等效
    /*
    int up=9;
    if(limit)
    {
        up=a[pos];
    }*/
    ll ans = 0;
    for(int i=0; i<=up; i++)
    {
        ans +=dfs(pos-1, lead&&i==0 ? 0:get_next(state,i) , lead&&i==0,limit&&i==a[pos]);
        ///上下等效
        /*if(lead&& i==0 )
        {
            ans += dfs(pos-1,state,lead&& i==0,limit&&i==a[pos]);
        }
        else
        {
            int next_state=get_next(state,i);
            ans += dfs(pos-1,next_state,lead&& i==0,limit&&i==a[pos]);
        }*/
    }
    if(!limit)
    {
        dp[pos][state][k] = ans;
    }
    return ans;
}
ll slove(ll x)
{
    if(x==0)return 0;
    pos = 0;
    while(x)
    {
        pos++;
        a[pos] = x%10;
        x /= 10;
    }
    return dfs(pos,0,true,true);
}
signed main()
{
    ll l,r;
    int T_T;
    scanf("%d",&T_T);
    memset(dp,-1,sizeof(dp));
    int tot=0;
    while(T_T--)
    {
        tot++;
        cin>>l>>r>>k;
        printf("Case #%d: ",tot);
        cout<<slove(r)-slove(l-1)<<'\n';
    }
}

kuangbin专题dp入门

(HDU)Max Sum Plus Plus(有点麻烦)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=1e6+10;
const ll int INF=0x7fffffff;

int a[maxn] = { 0 };
int dp[maxn] = { 0 };
///前i个数分组
int maxx[maxn] = { 0 };
///maxx[j]表示前i-1个数,分组为j个的最大值
int main()
{
    int n,m;
    while(scanf("%d %d",&m,&n)!=EOF)
    {
        dp[0] = 0;
        maxx[0] = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            dp[i] = 0;
            maxx[i] = 0;
        }
        int maxxnow=-INF;
        for(int j=1; j<=m; j++)///分为j组
        {
            maxxnow=-INF;
            for(int i=j; i<=n; i++)///前i个数,至少要有j个数才可以分成j组
            {
                dp[i]=max(dp[i-1],maxx[i-1])+a[i];
                ///当前的dp[i-1]表示的是前i-1个数被分为j个组,a[i]在第j个组
                ///maxx[i-1]表示的是当前i-1个数被分为j组,然后a[i]为第j组
                maxx[i-1]=maxxnow;
                ///当i=j的时候,i-1个数无法分j组当然是-INF啦
                ///利用循环更新maxx的值,为下一次dp循环做准备
                maxxnow=max(maxxnow,dp[i]);
            }
        }
        printf("%d\n",maxxnow);
    }
}

(HDU)Doing Homework

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=20;
const ll int INF=0x7fffffff;
struct node
{
    string name;
    int d,c;
}way[maxn];
ll dp[ (1<<maxn) ] = { 0 };
int t[ (1<<maxn) ] = { 0 };
int path[ (1<<maxn) ]={0};
int tot;
void print(int i)
{
    if(path[i]==0||i==0)return ;
    int before=1<<(path[i]-1);
    print(i-before);
    cout<<way[ path[i] ].name<<'\n';
}
inline void slove()
{
    int n;
    scanf("%d",&n);
    getchar();
    for(int i=1; i<=n; i++)
    {
        cin>>way[i].name>>way[i].d>>way[i].c;
    }
    tot = (1<<n);
    path[0] = 0;
    for(int i=1;i<tot;i++)
    {
        dp[i] = INF;
        path[i] = 0;
        for(int j=1;j<=n;j++)
        {
            int before = 1<<(j-1);
            if(i&before)
            {
               int nowtime = t[i^before] + way[j].c-way[j].d;
               if( nowtime <= 0 )
               {
                 nowtime = 0;///会罚钱的时间
               }
               if(nowtime+dp[i^before]<=dp[i])///1~n按的是字典树增大的顺序,所以值一样的时候,将字典序大的科目 放在每次状态的最后
               {
                   dp[i] = nowtime+dp[i^before];
                   t[i] = t[i^before] + way[j].c;
                   path[i] =j;
               }
            }
        }
    }
    printf("%lld\n",dp[tot-1]);
    print(tot-1);

}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        slove();
    }
}

(HDU)Super Jumping! Jumping! Jumping!(水题,最大上升子序列的变异体)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e3+10;
const ll int INF=0x7fffffff;
int a[maxn] = { 0 };
ll dp[maxn] = { 0 };
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF,n)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        ll maxx = 0;
        for(int i=1;i<=n;i++)
        {
            dp[i] = a[i];
            for(int j=1;j<i;j++)
            {
                if(a[j]<a[i])
                {
                    dp[i]=max(dp[i],a[i]+dp[j]);
                }
            }
            maxx=max(dp[i],maxx);
        }
        printf("%lld\n",maxx);
    }
}

(HDU)Piggy-Bank(背包问题,无限背包,这波就完事了~)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e5+10;
const ll int INF=0x3f3f3f3f3f3f3f3f3f;
int n;
int tot = 0;
int sum = 0;
ll p[maxn] = { 0 };
ll w[maxn] = { 0 };
ll dp[maxn] = { 0 };
void slove()
{
    ll bw,ew;
    scanf("%lld %lld",&bw,&ew);
    ll totw = ew-bw;

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&p[i],&w[i]);
    }
    memset( dp,INF,sizeof(dp) );
    /// bug(dp[1]);
    ///bug(INF);
    dp[0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=w[i];j<=totw;j++)
        {
            dp[j]=min(dp[j],dp[ j-w[i] ]+p[i]);
        }
    }

    if(dp[totw]==INF)
    {
        printf("This is impossible.\n");
        return ;
    }
    printf("The minimum amount of money in the piggy-bank is %lld.\n",dp[totw]);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        slove();
    }
}

(HDU)免费馅饼

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=1e5+10;
ll mp[maxn][12]={0};///防止数组越界
ll dp[maxn][12]={0};
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF,n)
    {
        int T = 0;
        memset(mp,0,sizeof(mp) );
        memset(dp,0,sizeof(dp) );
        for(int i=1; i<=n; i++)
        {
            int t,pos;
            scanf("%d %d",&pos,&t);
            mp[t][pos]++;
            T=max(T,t);
        }
        for(int i=T;i>=0;i--)
        ///倒着搜,dp[i][j]就是从T到i秒倒着走到j点时,最大的获得馅饼数
        ///这样子就没有起点的限制,更加方便,最后的终点就是dp[0][5];
        {
            for(int pos=0;pos<=10;pos++)
            {
                dp[i][pos]=max(dp[i+1][pos],dp[i+1][pos+1]);
                if(pos)///同防止越界
                {
                    dp[i][pos]=max(dp[i][pos],dp[i+1][pos-1]);
                }
                dp[i][pos]+=mp[i][pos];
                ///注意加上自己所处位置的馅饼哦~
            }
        }
        printf("%lld\n",dp[0][5]);
    }
}

Tickets(线性dp,普普通通)

每一元素只有两种情况,直接取或者和前面一个一起取

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e3+10;
const ll int INF=0x3f3f3f3f;
int a[maxn] = { 0 };///单个a[i]s所花的时间
int hb[maxn] = { 0 };///合并两个a后花的时间
ll dp[maxn] = { 0 };

inline void slove()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d",&hb[i]);
        ///合并i和i+1;
    }
    dp[0] = 0;
    dp[1] = a[1];
    for(int i=2;i<=n;i++)
    {
       dp[i]=min(dp[i-1]+a[i],dp[i-2]+hb[i-1]);
    }
    ll ans=dp[n];
    int h=8+ans/3600;
    int m=ans/60%60;
    int s=ans%60;
    if(h>12)
    {
        printf("%02d:%02d:%02d pm\n",h-12,m,s);
    }
    else
    {
        printf("%02d:%02d:%02d am\n",h,m,s);
    }
}
int main()
{
   int T;
   scanf("%d",&T);
   while(T--)
   {
       slove();
   }
}


最少拦截系统 (根据dilworth定理)

不下降子序列最小个数等于最大上升子序列的长度
不上升子序列的最小个数大于最大下降子序列的长度

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e3+10;
const ll int INF=0x3f3f3f3f;
int a[maxn] = { 0 };
int dp[maxn] = { 0 };
int maxx = 0;
int main()
{
   int n;
   while( scanf("%d",&n)!=EOF)
   {
       for(int i=1;i<=n;i++)
       {
           scanf("%d",&a[i]); dp[i]=1;
       }
       maxx = 0;
       for(int i=1;i<=n;i++)
       {
          
           for(int j=1;j<i;j++)
           {
               if(a[j]<a[i])
               {
                   dp[i]=max(dp[i],dp[j]+1);
               }
           }
           maxx=max(maxx,dp[i]);
       }
       printf("%d\n",maxx);
   }
}


(HDU)FatMouse’s Speed(寻找dp路径的好办法,相当nice,类似于递归的操作)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define bug(x) cout<<#x<<" == "<<x<<endl;
const int maxn=2e3+10;
const ll int INF=0x3f3f3f3f;
int tot = 0;
int dp[maxn] = { 0 };
struct node
{
    int w,s;
    int index;
    bool operator<(node b)
    {
        if(w!=b.w)
        {
            return w<b.w;///贪心一波
        }
    }
} a[maxn];
vector<int> way;
int main()
{
    int w,s;
    while(scanf("%d %d",&w,&s)!=EOF)
    {
        tot++;
        a[tot].index = tot;
        a[tot].w = w;
        a[tot].s = s;
        ///if(tot==9)break;
    }
    sort(a+1,a+1+tot);
    int maxt=1;
    for(int i=1; i<=tot; i++)
    {
        dp[i] = 1;
        for(int j=1; j<i; j++)
        {
            if(a[j].w<a[i].w&&a[j].s>a[i].s)
            {
                dp[i]=max(dp[i],dp[j]+1);
               if(dp[i]>dp[maxt])
				  maxt=i;
            }
        }
    }
    printf("%d\n",dp[maxt]);
    int sum=dp[maxt];
    way.push_back(maxt);
    for(int i=maxt-1;i>=1;i--)
    {
        if(dp[maxt]==dp[i]+1)
        {
            way.push_back(i);
            maxt = i;
        }
    }
    for(int i=sum-1;i>=0;i--)
    {
        int v=way[i];
        printf("%d\n",a[v].index);
    }
}

FatMouse and Cheese(记忆化搜索)

#include<iostream>
#include<cstdio>
#include<string>
#define bug(x) cout<<#x<<" == "<<x<<endl;
#define ll long long
using namespace std;
const int maxn=3e3+10;
#define INF 0x3f3f3f3f
int a[maxn][maxn] = { 0 };
int dp[maxn][maxn] = { 0 };
int dx[4]= {0,0,1,-1};
int dy[4]= {1,-1,0,0};
int n,k;
bool judge(int x,int y)
{
    return x>=0&&x<n&&y>=0&&y<n;
}
int dfs(int x,int y)
{
    if(dp[x][y])return dp[x][y];
    int maxx=0;
    for(int i=0; i<4; i++)
    {
        for(int j=1; j<=k; j++)
        {
            int tx=x+dx[i]*j;
            int ty=y+dy[i]*j;
            if(judge(tx,ty)&&a[tx][ty]>a[x][y])
            {
                int now = dfs(tx,ty);
                maxx=max(maxx,now);
            }
        }
    }
    dp[x][y]=maxx+a[x][y];
    return dp[x][y];
}
int main()
{

    while(scanf("%d %d",&n,&k)!=EOF&&n!=-1&&k!=-1)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                dp[i][j]=0;
                scanf("%d",&a[i][j]);
            }
        }
        dfs(0,0);
        cout<<dp[0][0]<<'\n';
    }
}

(HDU)Phalanx

#include<bits/stdc++.h>
#include<string>
#define bug(x) cout<<#x<<" == "<<x<<endl;
#define ll long long
using namespace std;
const int maxn = 2e3 + 10;
#define INF 0x3f3f3f3f
char a[maxn][maxn] = { 0 };
int dp[maxn][maxn] = { 0 };
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF && n)
    {

        if(n==0)break;
        for(int i=0; i<n; i++)
            scanf("%s",a[i]);
        memset(dp,0,sizeof(dp));
        int ans=1;
        for(int i=0; i<n; i++)
        {
            for(int j=n-1; j>=0; j--)
            {
                dp[i][j] = 1;
                if( i == 0 || j == n-1) continue;
                int t = dp[i-1][j+1];
                for(int k=1; k<=t; k++)
                {
                    if(a[i-k][j] == a[i][k+j] ) dp[i][j]++;
                    else break;
                }
                ans = max(dp[i][j],ans);
            }
        }
        cout<<ans<<endl;
    }
}


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值