1. 程式人生 > 其它 >2022/2/22

2022/2/22

2022/2/22

明七暗七_牛客競賽動態規劃專題班數位dp練習 (nowcoder.com)

二分+數位dp

二分割槽間右端點 r

用數位dp求出 r 與 m 之間有多少個合法數字

參考程式碼

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
 
const ll mod=20020219;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;
 
ll n,m,f[20][10],bit[20],len;

ll dfs(int pos,ll num,ll lim){
	if(!pos) 	return num%7!=0;	
	if(!lim && f[pos][num]) return f[pos][num];
	ll ans=0;
	int up=lim ? bit[pos] : 9;
	for(int i=0;i<=up;++i){
		if(i==7) continue;
		
		ans+=dfs(pos-1,(num*10+i)%7,lim&&i==up);
	}
	if(!lim) f[pos][num]=ans;
	return ans;
}

ll solve(ll x){
	if(x<0) return 0;
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	return dfs(len,0,1);
}

int ck(ll x){
	ll p=solve(x)-solve(m);
	p=x-m-p;
	return p>=n;
}

int main(){
	
	m=read(),n=read();	
    ll ans,l,r;
	l=m-1,r=1e16;
    while(l<=r){
    	ll md=(l+r)/2;
    	if(ck(md)) ans=md,r=md-1;
    	else l=md+1;
	}
    cout<<ans<<"\n";
    return 0;
}

CQOI2016 手機號碼_數位dp練習 (nowcoder.com)

數位dp

\(f[pos][ppre][pre][is4][is8][fg]\)表示當前位置pos,前一位是pre,前前一位是ppre,是否有4或8,是否有貢獻

參考程式碼

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
 
const ll mod=20020219;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;

ll f[13][13][13][2][2][2],n,m,bit[13],len;

ll dfs(ll pos,ll pre,ll ppre,ll fg,ll is4,ll is8,ll lim){
	if(is4&&is8) return 0;
	if(!pos) return fg;
	if(!lim && f[pos][ppre][pre][is4][is8][fg]) return f[pos][ppre][pre][is4][is8][fg];
	ll up=lim ? bit[pos] : 9;
	ll ans=0;
	for(int i=0;i<=up;++i){
		ll p=fg;
		if(i==pre&&ppre==pre) p=1;
		ans+=dfs(pos-1,i,pre,p,is4||i==4,is8||i==8,lim&&i==up);
	}
	if(!lim) f[pos][ppre][pre][is4][is8][fg]=ans;
	return ans;
}

ll solve(ll x){
	if(x<0) return 0;
	len=0;
	memset(f,0,sizeof(f));
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	ll ans=0;
	for(int i=1;i<=bit[len];++i){
		ans+=dfs(len-1,i,0,0,i==4,i==8,i==bit[len]);
	}
	return ans;
}

int main(){
	n=read(),m=read();
	ll ans=solve(m)-solve(n-1); 
	cout<<ans<<"\n";
    return 0;
}

\(f[pos][pre][cnt][sta][fg]\) 表示當前位pos,前一位為pre,有連續cnt個,4和8的狀態為sta,是否已經產生貢獻fg。

參考程式碼

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
 
const ll mod=20020219;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;

ll f[13][13][13][5][2],n,m,bit[13],len;

ll dfs(ll pos,ll pre,ll cnt,ll sta,ll fg,ll lim){
	if(sta==3) return 0;
	if(!pos) {
		return fg;	
	}
	if(!lim && f[pos][pre][cnt][sta][fg]) return f[pos][pre][cnt][sta][fg];
	ll up=lim ? bit[pos] : 9;
	ll ans=0;
	for(int i=0;i<=up;++i){
		ll ft=sta;
		if(i==4){
			if(!sta) sta=1;
			else if(sta==2) sta=3;
		}
		if(i==8){
			if(!sta) sta=2;
			else if(sta==1) sta=3;
		}
		ll p=(i==pre) ? cnt+1 : 1;
		p=min(p,3ll);
		ans+=dfs(pos-1,i,p,sta,fg||p==3,lim&&i==up);
		sta=ft;
	}
	if(!lim) f[pos][pre][cnt][sta][fg]=ans;
	return ans;
}

ll solve(ll x){
	if(x<0) return 0;
	len=0;
	while(x){
		bit[++len]=x%10;
		x/=10;
	}
	ll ans=0;
	for(int i=1;i<=bit[len];++i){
		ll sta=0;
		if(i==4) sta=1;
		if(i==8) sta=2;
		ans+=dfs(len-1,i,1,sta,0,i==bit[len]);
	}
	return ans;
}

int main(){
	n=read(),m=read();
	ll ans=solve(m)-solve(n-1); 
	cout<<ans<<"\n";
    return 0;
}

ps:找了三個小時bug,寫了好多組資料,終於找到了問題,之前記憶化陣列f沒有記錄fg那一維,但也感覺沒毛病,後來想了想,不記錄fg那一維,會導致pos前已經有貢獻的加到之前沒有貢獻的,貢獻會增多。

[網路流24題]運輸問題 (nowcoder.com)

費用流(最小費用流&最大費用流)

最大費用流就是最小費用流中每個流的費用取相反數,最後結果再取相反數

建模方法:

  1. s向每個倉庫連{c,0}的邊
  2. 每個倉庫向每個商店連{inf,w}的邊
  3. 每個商店向t連{c,0}的邊

去相反數跑兩邊最小費用流即可

參考程式碼

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
 
const ll mod=20020219;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;

int n,m,s,t; 
int p,head[qs],nxt[qs],to[qs],val[qs],dis[qs];
int a[107],b[107],c[107][107];
void add(int fx,int tx,int dx,int cx){
	to[p]=tx;
	dis[p]=dx;
	nxt[p]=head[fx];
	val[p]=cx;
	head[fx]=p++;
}

void ist(int u,int v,int w,int c){
	add(u,v,w,c);
	add(v,u,0,-c);
}

void build_map(int ff){
	memset(head,-1,sizeof(head));
	s=0,t=n+m+1;  p=0;
	for(int i=1;i<=n;++i){
		ist(s,i,a[i],0);
	}
	for(int i=1;i<=m;++i){
		ist(n+i,t,b[i],0);
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			ist(i,n+j,inf,c[i][j]*ff);
		}
	}
}

int inq[qs],d[qs],pre[qs];

bool spfa(){
	for(int i=0;i<=n+m+3;++i){
		inq[i]=0;d[i]=inf,pre[i]=-1;
	}
	d[s]=0;inq[s]=1;
	queue<int> q; q.push(s);
	while(q.si){
		int u=q.front(); q.pop();
		inq[u]=0;
		for(int i=head[u];i!=-1;i=nxt[i]){
			if(dis[i]){//spfa是以費用val求最短路的,但流量不能忽略 
				int v=to[i];
				if(d[u]+val[i]<d[v]){
					d[v]=d[u]+val[i];
					pre[v]=i;
					if(!inq[v]){
						q.push(v);
						inq[v]=1;
					}
				}
			}
		}
	}
	return pre[t]!=-1;
}

ll costflow(){//計算最小費用最大流 
	ll ret=0,ans=0;
	while(spfa()){
		int flow=inf;
		for(int i=t;i!=s;i=to[pre[i]^1]){
			//計算當前增廣路的最小流量 
			flow=min(dis[pre[i]],flow);
		}
		ans+=flow;
		for(int i=t;i!=s;i=to[pre[i]^1]){
			dis[pre[i]]-=flow;
			dis[pre[i]^1]+=flow;
			ret+=val[pre[i]]*flow;
		}
	}
	return ret;
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=m;++i) b[i]=read();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j) c[i][j] = read();
	}
	build_map(1);
	ll ans;
	ans=costflow(); cout<<ans<<"\n";
	
	build_map(-1);
	ans=costflow()*-1; cout<<ans<<"\n";
	
    return 0;
}