题目链接

E - Dividing Chocolate

AtCoder Beginner Contest 159  (E,F(dp))_#include

题意:给你h长,w宽矩阵 的黑白巧克力,1代表是白色,0是黑色

每次可以行切一刀,列切一刀,一切就要切到底的那种

问最多切多少刀,使得每一块巧克力中白色的数量小于等于k个

做法:经典做法了,最近牛客就遇到了两次,由于n特别小,那么对行进行暴力,切,然后对列进行贪心切即可。

我行的暴力用了二进制枚举

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e3+10;
int a[20][N],n,m,k,b[20];
char s[N];
int sum[20][N];
struct node
{
    int l1,l2;
}c[30];
int lenn;
int get(int l1,int r1,int l2,int r2)
{
    return sum[l2][r2]-sum[l1-1][r2]-sum[l2][r1-1]+sum[l1-1][r1-1];
}
int run()
{
    int r1=1,r2=1;
    int t=0;
    for(int i=1;i<=m;++i){
        //printf("i:%d\n",i);
        int flag=1;
        //printf("i:%d r2:%d\n",i,r2);

        for(int j=1;j<=lenn&&flag;++j){
            if(get(c[j].l1,r1,c[j].l2,r2)<=k) {
                continue;
            }
            else{

                flag=0;
            }
        }
        if(!flag){
            if(r1==r2) return 1e9;
            t++;
            r1=r2;
            r2++;
        }
        else ++r2;
    }
    return t;
}
int main()
{
	cin>>n>>m>>k;
	int ss=0;
	rep(i,1,n)
	{
	    cin>>s+1;
	    rep(j,1,m)
	    {
            a[i][j]=s[j]-'0';
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        }
	}

	//printf("%d\n",get(1,3,3,5));
	int len=(1<<n)-1;
	int ans=1e9;
	for(int j=0;j<=len;++j)
    {

        for(int k=0;k<n;++k){if((j>>k)&1) b[k+1]=1;else b[k+1]=0;}

        int t=0;
        lenn=0;
        int pre=b[1],l2=1,l1=1;
        for(int k=2;k<=n;++k){
            if(b[k]==pre) l2++;
            else{
                t++;
                c[++lenn]={l1,l2};
                l1=k;l2=k;
                pre=b[k];
            }
        }
        c[++lenn]={l1,l2};
        t+=run();
        //printf("j:%d t:%d lenn:%d\n",j,t,lenn);
        ans=min(ans,t);
    }
    printf("%d\n",ans);

}

F - Knapsack for All Segments

AtCoder Beginner Contest 159  (E,F(dp))_C_02

做法:设dp[i][j] 为当前序列以a[i]结尾时序列和为j的左下标之和。

那么答案就是AtCoder Beginner Contest 159  (E,F(dp))_c++_03

转移方程为:

AtCoder Beginner Contest 159  (E,F(dp))_C_04

用另一个数组f[i]维护AtCoder Beginner Contest 159  (E,F(dp))_C_05这个就可以O(NS)的复杂度

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair<int, int>
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=3e3+10;
const ll mod=998244353;
int a[N],n,s,len;
ll dp[N][N],f[N];

int main()
{
    cin>>n>>s;
    rep(i,1,n)
    {
        cin>>a[i];
    }

    for(int i=1;i<=n;++i){
        dp[i][a[i]]=i;
        for(int j=0;j<=s;++j){
            if(j+a[i]>s) continue;
            dp[i][j+a[i]]+=f[j];
            dp[i][j+a[i]]%=mod;;
        }
        for(int j=0;j<=s;++j) f[j]+=dp[i][j],f[j]%=mod;
    }
   

    ll ans=0;
    for(int i=1;i<=n;++i){
        ans+=(1ll*n-i+1)*dp[i][s]%mod;
        ans%=mod;
    }
    cout<<ans<<endl;
}