cf401D 状态压缩dp好题,每次把新加入集合的数字放在最后即可
/* 它可以通过重新排列数字n, 它没有任何前导零, x除以m后的余数等于0. 每次把新加的数放在最后 dp[i][j]表示状态i下模m=j的数量 dp[i|(1<<k)][j*10+a[k]]+=dp[i][j]; */ #include<bits/stdc++.h> using namespace std; #define ll long long ll len,n,m,a[20]; ll dp[1<<19][100]; int main(){ cin>>n>>m; while(n)a[++len]=n%10,n/=10; dp[0][0]=1; for(int i=0;i<(1<<len)-1;i++) for(int j=0;j<m;j++) for(int k=0;k<len;k++){ if(i&(1<<k))continue; if(i==0&&a[k+1]==0)continue;//前导0 dp[i|(1<<k)][(j*10+a[k+1])%m]+=dp[i][j]; } ll cnt[10]={},f[20]={},ans=dp[(1<<len)-1][0]; f[0]=f[1]=1; for(int i=2;i<=18;i++)f[i]=f[i-1]*i; for(int i=1;i<=len;i++)cnt[a[i]]++; for(int i=0;i<10;i++) if(cnt[i])ans/=f[cnt[i]]; cout<<ans<<endl; }
cf959E 思维题
#include<bits/stdc++.h> using namespace std; #define ll long long ll n,ans; ll lowbit(ll x){return x&(-x);} int main(){ cin>>n; ll tmp=n,cnt=1; while(tmp){ ans+=tmp/2*cnt; tmp/=2,cnt*=2; } if(n==lowbit(n)){ cout<<ans<<endl; return 0; } while(n){ n-=lowbit(n); ans+=lowbit(n); } cout<<ans<<endl;return 0; }
cf598E dp好题,就是最经典的线性dp,想通了挺简单的
/* dp[i][j][k]在i*j的方块里吃k大小的代价 */ #include<bits/stdc++.h> using namespace std; int dp[31][31][51]; int main(){ for(int i=0;i<=30;i++) for(int j=0;j<=30;j++) for(int k=0;k<=50;k++){ if(k==0 || k==i*j){ dp[i][j][k]=0; continue; } else dp[i][j][k]=100000000; for (int h = 0; h <= k; h++){ for (int m = 1; m < j; m++) dp[i][j][k] = min(dp[i][j][k], dp[i][m][h] + dp[i][j - m][k - h] + i*i); for (int m = 1; m < i; m++) dp[i][j][k] = min(dp[i][j][k], dp[m][j][h] + dp[i - m][j][k - h] + j*j); } } int t,n,m,k; cin>>t; while(t--){ cin>>n>>m>>k; cout<<dp[n][m][k]<<endl; } }
cf886D 字符串乱搞+判环
#include<iostream> #include<algorithm> #include<string> #include<string.h> using namespace std; string s,ans[28]; int main() { int i,j,n,f,x,len; int vis[28],chu[28],ru[28],mp[28][28]; while(cin>>n) { f=1; memset(mp,0,sizeof(mp)); //字符构成的图 memset(ru,-1,sizeof(ru)); //每个字符的入度 memset(chu,-1,sizeof(chu)); //每个字符的出度 //在下面这个循环中,chu/ru=0则说明字符出现过,chu/ru=-1则没出现过 for(i=0;i<n;i++) { cin>>s; //输入字符串 memset(vis,0,sizeof(vis)); //用于判断每个字符串中字符出现的次数 len=s.length(); vis[s[0]-‘a‘]++; //首个字符出现次数+1 ru[s[0]-‘a‘]=0; //首个字符出现过 chu[s[0]-‘a‘]=0; //首个字符出现过 for(j=1;j<len;j++) { //从第 1 个字符开始建图 int u=s[j-1]-‘a‘; //前一字符 int v=s[j]-‘a‘; //当前字符 mp[u][v]=1; vis[v]++; if(vis[v]>1) f=0; //如果出现次数>1,则输出 NO ru[v]=0; chu[v]=0; } } int next[28]; //每个字符的后继字符 memset(next,-1,sizeof(next)); for(i=0;i<26;i++) for(j=0;j<26;j++) if(mp[i][j]) { //如果存在边 ru[j]++; //入度+1 chu[i]++; //出度+1 next[i]=j; //记录后继 } for(i=0;i<26;i++) if(ru[i]>1||chu[i]>1) { //如果有字符的出度或入度 >1则输出 NO f=0; break; } for(i=0;i<26;i++) { //判断是否有环 x=i; memset(vis,0,sizeof(vis)); while(x!=next[x]) { if(vis[x]==1) { //如果当前元素访问过则有环 f=0; break; } vis[x]=1; x=next[x]; } } if(n>351 || !f) { cout<<"NO"<<endl; continue; } int tol=0; for(i=0;i<26;i++) //遍历 26个字符 if(ru[i]==0) { //如果入度为 0,则会形成一串字符 ans[tol]=‘a‘+i; x=i; while(next[x]!=-1) { //加入后继字符 char c=next[x]+‘a‘; ans[tol]+=c; x=next[x]; } tol++; } sort(ans,ans+tol); //排序 for(i=0;i<tol;i++) cout<<ans[i]; cout<<endl; } return 0; }
原文:https://www.cnblogs.com/zsben991126/p/10680320.html