DP杂题

1.http://codeforces.com/problemset/problem/946/D

/*
*************************************************** 
刚开始写的是贪心,WA了
正解是DP
val[i][j]表示第i层删了j个最小花费
f[i][j]表示到了第i层,删了j个,最小花费
f[i][j]=min(f[i][j],f[i-1][p]+val[i][j-p]);
把每一层看作一组,有点像分组背包(好吧,不能分组背包,每一层都有贡献,实测WA了) 
*************************************************** 
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
const int N=1000;
struct node{int pos,val,lc,rc,shit;};
int n,m,k,tot=0;
char a[N][N];
priority_queue<node> q;
bool operator<(const node &x,const node &y){return x.val<y.val||(x.val==y.val&&x.shit>y.shit);}
void find_s_t(char *a,int &l,int &r){
	
	for(int i=1;i<=m;i++)if(a[i]=='1'){l=i;break;}
	for(int i=m;i>=1;i--)if(a[i]=='1'){r=i;break;}
}
void work(char *a,int pos,int st,int ed,int k){
	if(st==ed){q.push((node){pos,1,0,0});return;}
	if(st==0&&ed==0)return;
	int i=st+1;for(i;i<=m;i++)if(a[i]=='1')break;
	int j=ed-1;for(j;j>=1;j--)if(a[j]=='1')break;
	if(ed-j>=i-st){
		int sum=0;
		for(int k=j;k>=1;k--)if(a[k]=='0')break;else sum++;
		q.push((node){pos,ed-j,st,j,sum});
	}else{
		int sum=0;
		for(int k=i;k<=m;k--)if(a[k]=='0')break;else sum++;
		q.push((node){pos,i-st,i,ed,sum});
	}
	if(k==1)tot+=ed-st+1;
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d%d%d",&n,&m,&k);
	rep(i,1,n)scanf("%s",a[i]+1);
	rep(i,1,n){int st=0,ed=0;find_s_t(a[i],st,ed);work(a[i],i,st,ed,1);}
	rep(i,1,k){
		node x=q.top();q.pop();
		if(x.lc==0&&x.rc==0){tot-=1;continue;}
		tot-=x.val;
		work(a[x.pos],x.pos,x.lc,x.rc,0);
	}
	cout<<tot;
	return 0;
}
*/
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=510;
int val[N][N],f[N][N],SL[N],SR[N],sum[N],ans=1e9,n,m,k;
char a[N][N];
int main()
{
	//freopen("a.in","r",stdin);
	scanf("%d%d%d",&n,&m,&k);
	rep(i,1,n)scanf("%s",a[i]+1);
	memset(f,0x3f,sizeof(f));
	memset(val,0x3f,sizeof(val));
	rep(i,1,n){
		rep(j,1,m)SL[j]=SL[j-1]+(a[i][j]=='1');
		per(j,m,1)SR[j]=SR[j+1]+(a[i][j]=='1');
		rep(j,1,m)sum[i]+=(a[i][j]=='1');
		rep(l,1,m){
			rep(r,l,m){
				if(l==r)val[i][SL[l]+SR[r+1]]=min(val[i][SL[l]+SR[r+1]],0);
				if(r==l&&a[i][r]=='0')
				val[i][SL[l-1]+SR[r+1]]=min(val[i][SL[l-1]+SR[r+1]],0);
				else 
				val[i][SL[l-1]+SR[r+1]]=min(val[i][SL[l-1]+SR[r+1]],r-l+1);
			}
		}
	}
	rep(i,0,k)f[0][i]=0;
	rep(i,1,n)rep(p,0,sum[i])per(j,k,p)
	f[i][j]=min(f[i][j],f[i-1][j-p]+val[i][p]);
	cout<<f[n][k];
	return 0;
}

2.https://www.lydsy.com/JudgeOnline/problem.php?id=1226

/*
感觉是状压神题
f[i][S][k]表示i之前的人都吃过了,i及i之后的人吃饭的状态是S,上一个吃饭的人与i的距离是k 的最小代价
我们可以发现这个状态设计非常的好(QAQ)首先考虑i是阶段,肯定是递增的,[S][k]是每个状态,与答案有着密切的关系
然后我们考虑如何转移
其实和顺序有关的问题可以用状态压缩,因为我们考虑一个集合,加入元素的顺序不同,显然答案也不同,所以我们每一个状态
保存的就是加入 属于集合S的元素 后的最优解 
然后我们考虑如何转移(QAQ)
对于一个状态,那我们考虑肯定枚举i后面的人让他先吃饭,于是就
f[i][S|(1<<r)][r]=min(f[i][S|(1<<r)][r],f[i][S][k]+val(i+k,i+r)); r是枚举的下一个吃饭的人,k是上一个吃饭的人
然后我们可以观察这个状态转移方程,显然就这一个是不行的,i显然转移不过来,那我们考虑什么时候可以推向下一个状态
我们再来看状态的定义: 表示i之前的人都吃过了,i及i之后的人吃饭的状态是S,上一个吃饭的人与i的距离是k 的最小代价
那么显然如果i吃过饭了,就可以愉快的转移啦,即
if(S&1) f[i+1][S>>1][k-1]=min(f[i+1][S>>1][k-1],f[i][S][k])
还有一点细节就是我们再枚举i后面的人的时候,要保证合法,所以记录一个目前能到达距离的最小值,如果超过这个最小值就break;
if(i+r>dis)break;
dis=min(dis,i+r+b[i+r]);
然后就可以Accept了 
*/
#include<bits/stdc++.h>
#define f(i,s,k) (g[i][s][k+8])
using namespace std;
int g[1010][300][20],a[1010],b[1010],T,n,inf;
int val(int x,int y){if(x==0)return 0;return a[x]^a[y];}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
        memset(g,10,sizeof(g));inf=g[0][0][0];f(1,0,-1)=0;
        for(int i=1;i<=n;i++){
            for(int s=0;s<(1<<8);s++){
                for(int k=-8;k<=7;k++){
                    if(f(i,s,k)>=inf)continue;
                    if(s&1)f(i+1,s>>1,k-1)=min(f(i+1,s>>1,k-1),f(i,s,k));
                    else{
                        int dis=inf;
                        for(int r=0;r<=7;r++){
                            if((s&(1<<r))==0){
                                if(i+r>dis)break;
                                dis=min(dis,i+r+b[i+r]);
                                f(i,s|(1<<r),r)=min(f(i,s|(1<<r),r),f(i,s,k)+val(i+k,i+r));
                            }
                        }
                    }
                }
            }
        }
        int ans=inf;
        for(int k=-8;k<=-1;k++)ans=min(ans,f(n+1,0,k));
        cout<<ans<<"\n";
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值