BZOJ 1021 :[SHOI2008]Debt 循环的债务 (DP)

真是蒟蒻,已经连续几题沦落到不看题解写不出的地步了,最近不太想写题了,学习新的算法吧

用f[i][j][k]表示算到第i种钱,乙有j元,丙有k元的最小方案数,记录可以转移的方案以及去掉不可以转移的方案就行了

CODE:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int f[7][1010][1010],t[4][7],sum[4];
int a[7]={0,1,5,10,20,50,100};
int x[4];
int c[4];
struct node{
 int x,y;
}q[101000],k[101000];
bool b[7][1010][1010];
int check(int x,int y,int z,int ans){
 if (b[x][y][z]) {
  f[x][y][z]=min(f[x][y][z],ans);
  return 0;
 }else {
  b[x][y][z]=1;
  f[x][y][z]=ans; 
 }
 if (x==6) return 0;
 if (x!=3){
  if (abs(sum[2]-y)%a[x+1]!=0) return 0;
  if (abs(sum[3]-z)%a[x+1]!=0) return 0;
  if (abs(sum[2]+sum[3]-y-z)%a[x+1]!=0) return 0;
 }
 k[0].x++;k[k[0].x]=(node){y,z};
 return 0;
}
int solve(int i,int j,int x,int y,int z){
 for (int k=0;k<=t[y][i];k++)
  for (int l=0;l<=t[z][i];l++) {
   c[x]=k+l;c[y]=-k;c[z]=-l;
   check(i,q[j].x+c[2]*a[i],q[j].y+c[3]*a[i],f[i-1][q[j].x][q[j].y]+k+l);
  }
 for (int k=0;k<=t[x][i];k++)
  for (int l=0;l+k<=t[x][i];l++){ 
   c[x]=-(k+l);c[y]=k;c[z]=l;
   check(i,q[j].x+c[2]*a[i],q[j].y+c[3]*a[i],f[i-1][q[j].x][q[j].y]+k+l);
  }
 return 0;
}
int main(){
 scanf("%d%d%d",&x[1],&x[2],&x[3]);
 for (int i=1;i<=3;i++)
 for (int j=1;j<=6;j++) {
  scanf("%d",&t[i][6-j+1]);
  sum[i]+=t[i][6-j+1]*a[6-j+1];
 }
 q[0].x=1;
 q[1]=(node){sum[2],sum[3]};
 sum[2]+=x[1]-x[2];
 sum[1]+=x[3]-x[1];
 sum[3]+=x[2]-x[3];
 for (int i=1;i<=6;i++){
  k[0].x=0;
  for (int j=1;j<=q[0].x;j++) {
   solve(i,j,1,2,3);
   solve(i,j,2,1,3);
   solve(i,j,3,1,2);   
  }
  swap(k,q);
 }
 if (b[6][sum[2]][sum[3]]) printf("%d",f[6][sum[2]][sum[3]]);
 else printf("impossible");
 return 0;
}

posted @ 2014-08-04 20:34  New_Godess  阅读(180)  评论(0编辑  收藏  举报