[典题]
codeforces 789E. The Great Mixing(经典,dp,状态图建立)

void bfs() { queue<int>q; for(int i=0;i<=1000;i++) { if(st[i]) { v.pb(n-i); int x=n-i+1000;//这里最主要的作用是标记,n-i可能会越界,加1000偏移量 f[x]=1; if(x==1000)break; q.push(x); } } while(!q.empty()) { auto t=q.front(); q.pop(); for(int i=0;i<v.size();i++) { int w=t+v[i]; if(w>=0&&w<=2000&&f[w]>f[t]+1) { f[w]=f[t]+1; if(w==1000)break; q.push(w); } } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n>>k; memset(f,INF,sizeof f); memset(st,0,sizeof st); for(int i=0;i<k;i++) { int x; cin>>x; st[x]=1; } bfs(); if(f[1000]>=INF) { cout<<-1<<endl; return 0; } else { cout<<f[1000]<<endl; } return 0; }
A. Cut Ribbon
完全背包问题,给出三个可以裁剪的长度,看最多能裁剪多少段
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n; memset(f,-0x3f,sizeof f); f[0]=0; for(int i=1;i<=3;i++) { int x; cin>>x; for(int j=x;j<=n;j++) f[j]=max(f[j],f[j-x]+1); } cout<<f[n]<<endl; return 0; }
Bigger is Better UVA - 12105 (数位dp)

i是火柴个数,j是每次从右取的数字的总值%m的余数,dp[][]=进行的次数
输出时从大到小减去次数-1输出对应的k值,应用pre记录此时的k
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int cnt=1; while(cin>>n&&n) { cin>>m; memset(f,-1,sizeof f); for(int i=0;i<=n;i++) f[i][0]=0; int ans=-1; for(int i=1;i<=n;i++)//枚举火柴 for(int j=0;j<m;j++)//j是枚举所有0~9的余数 for(int k=9;k>=0;k--)//枚举从大开始的个位拼的数字 if(i>=num[k]) { if(f[i-num[k]][(j*10+k)%m]>=0&&f[i][j]<(f[i-num[k]][(j*10+k)%m]+1)) { f[i][j]=f[i-num[k]][(j*10+k)%m]+1; pre[i][j]=k; } } ans=f[n][0]; cout<<"Case "<<cnt++<<": "; if(ans<=0) { cout<<-1<<endl; continue; } else { int x=n,y=0; for(int i=ans-1;i>=0;i--) { int t=pre[x][y]; cout<<t; x-=num[t]; y=(y*10+t)%m; } cout<<endl; } } return 0; }
遗迹探险(线性dp)
因为k传送门的数量很少,所以可以枚举传送门的位置,正着做一遍线性dp,反着做一遍,那么我们加上同一个传送门的位置的时候,可以直接进行取max
signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n>>m; vector<vector<int>>f(n+10,vector<int>(m+10,-INF)),g(n+10,vector<int>(m+10,-INF)); vector<vector<int>>a(n+10,vector<int>(m+10)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; f[0][1]=0; f[1][0]=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j]; g[n][m+1]=0; g[n+1][m]=0; for(int i=n;i>=1;i--) for(int j=m;j>=1;j--) g[i][j]=max(g[i+1][j],g[i][j+1])+a[i][j]; int t; cin>>t; while(t--) { int k; cin>>k; vector<PII>c(k); for(int i=0;i<k;i++) cin>>c[i].first>>c[i].second; int ans=f[n][m]; for(int i=0;i<k;i++) for(int j=0;j<k;j++) { if(i==j)continue; int aa=c[i].first,bb=c[i].second; int cc=c[j].first,dd=c[j].second; ans=max(ans,f[aa][bb]+g[cc][dd]); } cout<<ans<<endl; } return 0; }
Codeforces Round 871 (Div. 4)F. Forever Winter
给定一个图,请你求出 x,y ,满足在图上的某一个点 ver(中心点) 连接了 x 个点,那些与 ver 相连的点,除 ver 以外都与 y 个结点相连。
思路:
1.我们暴力枚举每个x=cnt[i](cnt[a]++存的是a点对应的点数),这时候判断剩下的点是否满足y==(n - 1 - x) / x,还要判断x<=1&&<=1
2.这时候我们根本不用和中心点相连的x,只需要进行从x点出发,每次遇到的点++后,最终边都遍历结束后判断num==tmp即可,就可以判断y是否成立,成立直接输出就好了
void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } void dfs(int u) { st[u] = 1; for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (st[j])continue; tmp++; dfs(j); } } bool check(int ver, int num) { tmp = 0; dfs(ver); return tmp == num; } void solve() { memset(h, -1, sizeof h); memset(cnt, 0, sizeof cnt); idx = 0; cin >> n >> m; for (int i = 1; i <= m; i++) { int a, b; cin >> a >> b; add(a, b); add(b, a); cnt[a]++; cnt[b]++; } for (int i = 1; i <= n; i++) { int x = cnt[i], y = (n - 1 - x) / x; if (x <= 1 || y <= 1)continue; if (y * x != n - 1 - x)continue; bool ok = true; memset(st, false, sizeof st); st[i] = true; for (int j = h[i]; j != -1; j = ne[j]) { int u = e[j]; if (!check(u, y)) { ok = false; break; } } if (ok) { cout << x << ' ' << y << endl; return; } } }

浙公网安备 33010602011771号